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.
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.
Point your SDK at us
Set
base_urltohttps://labs-api.petpooja.com/v1and pass your key as theapi_key. Everything else stays the OpenAI SDK you already use.Send a document
Attach an image as an
image_urlpart or a PDF as afilepart, both base64 data URIs. The response is a standard chat completion.
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.
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?"}],
)curl https://labs-api.petpooja.com/v1/chat/completions \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"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.
| Header | Direction | Description |
|---|---|---|
| Authorizationstring | request | Bearer <your_api_key>. Missing, malformed, or revoked keys return 401 invalid_api_key. |
| Content-Typestring | request | Always application/json. |
| X-Request-Idstring | response | Returned on every response, success or failure. Log it - it is the handle our support team traces. |
| Retry-Afterseconds | response | Sent with 429. Wait this long before retrying. |
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.
- 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.
- 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"