Browse the docsGetting started

Getting started

Quickstart

The Petpooja API extracts structured data from invoices, receipts and other documents. It is OpenAI-compatible: point any OpenAI SDK at our base URL, send an image or PDF, and read the result off a standard chat completion.

OpenAI-compatiblechitraguptaImages & PDFsStable · v1
  1. Contact us for an API key

    Keys are issued by our team. Reach out and we will provision one for your workspace, along with the page volume you need.

  2. Point your SDK at us

    Set base_url to https://labs-api.petpooja.com/v1 and pass your key as the api_key. Everything else stays the OpenAI SDK you already use.

  3. Send a document

    Attach an image as an image_url part or a PDF as a file part, both base64 data URIs. The response is a standard chat completion.

Base URL

https://labs-api.petpooja.com/v1 for OpenWorld, and https://labs-api.petpooja.com/v1/extract for Default. The path is what selects the endpoint - the model name never changes.

Petpooja LabsUse these APIs in the playgroundOpen Petpooja Labs to send a request against the same endpoints documented here.Open playground
import os
from openai import OpenAI

API_KEY  = "Your API Key"
BASE_URL = "https://labs-api.petpooja.com/v1" || "https://petpoojalabs-api.petpooja.com/v1/extract"
MODEL    = "chitragupta"

client = OpenAI(api_key=API_KEY, base_url=BASE_URL)

r = client.chat.completions.create(
    model="chitragupta",
    messages=[{"role": "user", "content": "What can you extract from an invoice?"}],
)

Authentication

Every request carries your key in an Authorization: Bearer header. There are no other auth schemes, and keys are scoped to a single workspace.

HeaderDirectionDescription
AuthorizationstringrequestBearer <your_api_key>. Missing, malformed, or revoked keys return 401 invalid_api_key.
Content-TypestringrequestAlways application/json.
X-Request-IdstringresponseReturned on every response, success or failure. Log it - it is the handle our support team traces.
Retry-AftersecondsresponseSent with 429. Wait this long before retrying.
Keep it server-side

Usage is billed per page against the key that signs the request. Never ship a key to a browser or mobile client - proxy through your own backend.

import os
from openai import OpenAI

API_KEY  = os.environ["PETPOOJA_API_KEY"]      # <- set this env var, don't paste the key here
BASE_URL = "https://labs-api.petpooja.com/v1"
MODEL    = "chitragupta"

client = OpenAI(api_key=API_KEY, base_url=BASE_URL)

def show(r):
    """Pretty-print a response: request_id (for support), finish_reason, usage, content."""
    print("request_id   :", r.id.replace("chatcmpl-", ""))
    print("finish_reason:", r.choices[0].finish_reason,
          "  ('length' = answer was cut off by max_tokens)")
    print("usage        :", r.usage)
    print("=" * 78)
    print(r.choices[0].message.content)

print("Client ready ->", BASE_URL)

Endpoint overview

Both endpoints run chitragupta and speak the same OpenAI wire format. They differ in who writes the prompt and how many model calls your document turns into.

OpenWorld
POST /v1/chat/completions
  • Ask for anything - JSON, a summary, or a single field.
  • Every page and image goes into one model call, so the model reasons across the whole document at once.
  • Response is based on what you asked for in the input (prompt and image).
  • Up to 10 pages or images per request.
Default
POST /v1/extract/chat/completions
  • Your text is ignored - the extraction prompt is fixed server-side and cannot be overridden.
  • Each image and each PDF page becomes its own model call, run concurrently.
  • Results come back source-tagged per page - pdf1_page1, image1.
  • Up to 25 pages or images per request.

Which one do I want?

Use OpenWorld for generic question-and-answer over a document. Use Default for extraction.

# OpenWorld — generic question and answer over a document.
BASE_URL = "https://labs-api.petpooja.com/v1/"

# Default — extraction with a fixed prompt, one call per page.
BASE_URL = "https://labs-api.petpooja.com/v1/extract"