Docs
5-minute quickstart
- Create an account and copy your API key from the Dashboard.
- Top up your wallet by UPI.
- Call any model endpoint with your key. Read the output and the signed receipt.
curl -X POST https://apinfy.com/v1/chat/completions \
-H "Authorization: Bearer APINFY_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-v4:flash","messages":[{"role":"user","content":"hello"}]}'Models
The model column is the exact string to send, and it is the real model's own name in name:tag form — no house branding in between, so deepseek-v4:flash in the quickstart above is exactly DeepSeek V4 Flash. The second column is the same model written out in full. Prices are rupees per 1M tokens, exclusive of GST, and this table is generated from config/models.yaml, which is the same file the router and the receipts read.
Read the lane column before you send anything sensitive. Lane R models run on GPUs geolocated in India, so the data stays in India — the operator and the host are still foreign companies. Lane G models are served from outside India: that traffic leaves the country and is not covered by any data-residency or DPDP claim made elsewhere on this site. Whichever ran is named in the signed receipt, so you can check it after the fact as well as before.
| model | Full name | Lane | ₹ / 1M in | ₹ / 1M out |
|---|---|---|---|---|
qwen3.8:27b | Qwen 3.8 27B (India) | R — Regional GPU in India | ₹30 | ₹90 |
bge-m3:latest | apinfy Embed (BGE-M3) | G — Global leaves India | ₹2 | — |
qwen3.5:397b | Qwen 3.5 397B | G — Global leaves India | ₹60 | ₹180 |
kimi-k2.7:code | Kimi K2.7 Code | G — Global leaves India | ₹91 | ₹384 |
deepseek-v4:flash | DeepSeek V4 Flash | G — Global leaves India | ₹42 | ₹127 |
qwen3.8:max | Qwen 3.8 Max | G — Global leaves India | ₹192 | ₹576 |
qwen3.7:plus | Qwen 3.7 Plus | G — Global leaves India | ₹45 | ₹135 |
Models still on the waitlist are not listed here. Anything absent from this table cannot be called yet.
These ids changed on 24 August 2026: they used to be house names like apinfy-flash. The old ids still resolve to the same models, so existing code keeps working, but they are no longer listed anywhere and will be retired — send the ids in the table above.
Auth
Send your key as a Bearer token: Authorization: Bearer kk_live_…. Sandbox endpoints (/v1/sandbox/{slug}) need no auth and use fixed samples.
Uploading files and images
Two endpoints take a file, both as multipart/form-data. Send audio straight to /v1/audio/transcriptions and get text back. For everything else, upload once to /v1/files and reuse the attachment_id it returns.
Do not set Content-Type yourself on these calls. Your HTTP client has to generate the multipart boundary; a hand-written header without one makes the server reject an otherwise valid upload.
1. Upload
curl -X POST https://apinfy.com/v1/files \ -H "Authorization: Bearer APINFY_KEY" \ -F "[email protected]" # {"attachment_id":"3f2a…","filename":"handbook.pdf", # "content_type":"application/pdf","size_bytes":89313,"sha256":"9c1d…", # "retention_mode":"ephemeral","expires_at":"2026-08-12T09:14:00Z"}
2. Use it
# Price it first — nothing is charged until you post the document.
curl -X POST https://apinfy.com/v1/datasets/DATASET_ID/estimate \
-H "Authorization: Bearer APINFY_KEY" -H "Content-Type: application/json" \
-d '{"attachment_id":"3f2a…"}'
curl -X POST https://apinfy.com/v1/datasets/DATASET_ID/documents \
-H "Authorization: Bearer APINFY_KEY" -H "Content-Type: application/json" \
-d '{"attachment_id":"3f2a…"}'Datasets are paused. The console at /datasets is showing a coming-soon page, and on a deployment with DATASETS_ENABLED=0 the two calls above are refused with E_DISABLED. Datasets you already have stay readable, searchable and deletable — only taking new documents in has stopped.
Python and JavaScript
# Python — requests builds the multipart body for you.
import requests
key = "APINFY_KEY"
up = requests.post("https://apinfy.com/v1/files",
headers={"Authorization": f"Bearer {key}"},
files={"file": open("handbook.pdf", "rb")}).json()
print(up["attachment_id"], up["sha256"])// JavaScript — let FormData set the boundary; do not add Content-Type.
const body = new FormData();
body.append("file", fileInput.files[0]);
const up = await fetch("https://apinfy.com/v1/files", {
method: "POST",
headers: { Authorization: `Bearer ${key}` },
body,
}).then((r) => r.json());What each type can actually do
Every type below uploads and is stored. They differ in what can then read them, so this table is the one worth checking before you build against it.
text/plain, text/csv, application/json | Indexed into a dataset and searchable. |
application/pdf | Uploads and is stored, but cannot currently be indexed: no PDF text extractor is installed on the live service, so posting one to a dataset returns E_INPUT, "no text extractor for this type: application/pdf". We would rather refuse than hand back invented text. |
audio/wav, audio/mpeg, audio/mp4, audio/ogg, audio/flac | Transcribed by /v1/audio/transcriptions. |
image/png, image/jpeg | Stored, hashed and retrievable — nothing more. There is no OCR and no vision model yet, so an image cannot be indexed into a dataset or read by a model. Posting one to /v1/datasets/{id}/documents returns E_INPUT, "no text extractor for this type: image/png". |
Limits and rules
- 25 MB per file. Anything larger is refused outright.
- Uploads expire after 24 hours. A dataset built from one does not. The chunks are the artefact — search keeps working long after the upload is gone.
- The content type you declare is not trusted. The server reads the file's leading bytes and uses those. A
.pngthat is really a zip is refused, whatever the filename and header say. - The
sha256you get back is the hash of the exact bytes stored, and it is the same hash that appears in the signed receipt — so you can prove which file a receipt refers to. - An attachment is readable only by the key that uploaded it. Another key gets a 404.
Error codes
E_AUTH | Missing or invalid API key (401). |
E_WALLET | Insufficient wallet balance / spend cap (402). |
E_INPUT | Input failed validation — size, type, schema, or dial (422). |
E_ENGINE | Engine failed after retries (502). |
E_VERIFY_FAILED_REFUNDED | Verified dial failed its checks — wallet auto-credited (200). |
E_RATE | Rate or sandbox cap exceeded (429). |
Verify a receipt (RASEED)
Every job returns a canonical JSON receipt signed with Ed25519. Fetch the public key from /.well-known/raseed.json, drop the signature field, canonicalize (JSON sort_keys, compact), and verify. An independent verifier ships at tests/verify_receipt.py.
python -m tests.verify_receipt receipt.json https://apinfy.com/.well-known/raseed.json # -> VERIFIED
API reference
API reference unavailable — gateway/openapi.json could not be read. Run make openapi.