The Pika API has one index and two documents that matter: the machine-readable llms.txt and the per-operation specification under dev.pika.art/llms/..., with an OpenAPI 3.1 file at dev.pika.art/openapi.json.
Where the official Pika API documentation lives
llms.txt opens with an instruction aimed at humans and agents alike: Do not infer endpoint paths, request fields, enum values, pricing, or model support from a model name or from prior knowledge. It then fixes an order of precedence.
- The catalog record,
GET /catalog/apis/{api_id}?expand=inputs, forcurrent availability, request fields, required inputs, enum values, and pricing metadata. - The operation’s
llms/...specification, forthe exact submit endpoint, request and response examples, polling behavior, uploads, and errors. llms.txtitselffor discovery and routing, not as a substitute for the selected operation's specification.
One tie-breaker outranks the rest: If a prose example conflicts with the catalog input_schema, follow the catalog schema. The catalog also settles the path shape: Send the slashes in api_id literally.
The three hops: submit, poll, fetch

Media operations are asynchronous, and the docs describe three calls: Submit: POST https://api.dev.pika.art/v1/media/{vendor}/{model}/{function}, then Poll: GET https://api.dev.pika.art/v1/media/jobs/{request_id} until status is completed or failed, after which a completed job carries its download URL in output.
# Hop 1 — submit. The body belongs to the operation, so read its spec first.
curl -s -X POST "https://api.dev.pika.art/v1/media/pika/pika-2.5/image-to-video" \
-H "X-API-Key: ${PIKA_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"resolution": "1080p", "duration_s": 5, "image": "https://example.com/frame.png"}'
# Hop 2 — poll the id from that response until the status is terminal.
curl -s "https://api.dev.pika.art/v1/media/jobs/${JOB_ID}" \
-H "X-API-Key: ${PIKA_API_KEY}"
# Hop 3 — read the finished URL. This answers 409 before the job completes.
curl -s "https://api.dev.pika.art/v1/media/jobs/${JOB_ID}/content" \
-H "X-API-Key: ${PIKA_API_KEY}"
The submit response is not the result: The submit response returns a job object containing id and status. Keep that id — it is the {request_id} every later call expects. The Pika API reference runs the same hops against Pikaframes.
Endpoint paths, copied from the docs
Every call uses the base URL https://api.dev.pika.art.
The media endpoints
| Method and path | What it does |
|---|---|
POST /v1/media/{vendor}/{model}/{function} | Submits a generation job. |
GET /v1/media/jobs/{request_id} | Polls the job. |
GET /v1/media/jobs/{request_id}/content | Returns the output URL as { "url": ... }. |
DELETE /v1/media/jobs/{request_id} | Erases media and prompt; answers 204. |
The supporting endpoints
| Method and path | What it does |
|---|---|
GET /catalog/apis | Lists callable operations; needs no key. |
POST /catalog/apis/{api_id}/quote | Prices a body without running it. |
GET /billing/balance | Reads the prepaid balance in micro-USD. |
The API key header, and the header Google users try first
The docs compress authentication into one line: Authentication header: X-API-Key: $PIKA_API_KEY or Authorization: Bearer $PIKA_API_KEY. Google's x-goog-api-key is not accepted. The third clause is the one that costs an afternoon.
Keys are minted at dev.pika.art/keys, and PIKA_API_KEY is the recommended secret name. The handling rule is flat: Never place an API key in browser code, a URL, source control, generated output, screenshots, or chat. A browser app must call Pika through its own authenticated server route.
Two calls need no key and double as a connectivity check.
# The public catalog needs no API key.
curl -s "https://api.dev.pika.art/catalog/apis" | jq .
# The OpenAPI 3.1 file declares the accepted auth schemes and every callable path.
curl -s https://dev.pika.art/openapi.json | jq '.components.securitySchemes'
Polling a media job
status has four values — queued, running, completed, failed — and only the last two are terminal. A rejected submit still returns a job object, with status of failed and error set, so one check covers success and refusal alike.
import os
import time
import requests
BASE = "https://api.dev.pika.art"
HEADERS = {"X-API-Key": os.environ["PIKA_API_KEY"]}
TERMINAL = {"completed", "failed"}
def wait_for(job_id, timeout_s=900, interval_s=5):
deadline = time.monotonic() + timeout_s
while True:
job = requests.get(f"{BASE}/v1/media/jobs/{job_id}", headers=HEADERS, timeout=30).json()
if job["status"] in TERMINAL:
return job
if time.monotonic() > deadline:
raise TimeoutError(f"{job_id} still {job['status']} after {timeout_s}s")
time.sleep(interval_s)
job = wait_for(os.environ["JOB_ID"])
if job["status"] == "failed":
# error.code is the stable value to branch on; error.message varies.
raise SystemExit(job["error"]["code"])
print(job["output"]["video"]["url"])
A terminal job looks like this:
{
"id": "media_8f3a2c91-5b7d-4e0a-9c26-31d4f2a8e6b0",
"status": "completed",
"output": {
"media_type": "video",
"video": { "url": "https://api.dev.pika.art/v1/files/video_8f3a2c91.mp4" }
}
}
Two details bite in production: the content endpoint answers 409 before the job completes, and error.code is the stable machine-readable value to branch on while error.message varies. A webhook replaces the loop — pass a top-level "webhook_url" on the submit body (No registration, and a rejected URL answers 400) or register an endpoint on the Webhooks page. Deliveries are signed per Standard Webhooks, retried over about 55 hours, and deduplicated on webhook-id; the signing secret is read by an owner or admin, never with an API key.
Free endpoints, and the gaps in the documentation
The catalog, the quote endpoint and the billing reads are never charged, and There is no free generation tier and no separate test environment. The distance between a quote and the settled charge is what the Pika API pricing breakdown explains.
Three things the docs do not state: the request and concurrency limits behind a 429, how long a completed job is retained, and how long an output URL stays valid. No provider-specific SDK is published, though the docs note that No provider-specific SDK is required. Retry safety is covered: send a stable Idempotency-Key with the same body and Never reuse an idempotency key with a different body; a failed job replays on its key, so retry with a fresh one. Output length and resolution are capped per model, so the duration and resolution matrix belongs beside the consumer credit model.
Frequently asked questions
Which Pika document should I trust? The catalog record first, then the operation’s llms/... specification, with llms.txt for routing only.
What is the base URL and the auth header? https://api.dev.pika.art, with X-API-Key: $PIKA_API_KEY or Authorization: Bearer $PIKA_API_KEY. Google's x-goog-api-key is not accepted.
How do I poll a media job? Call GET /v1/media/jobs/{request_id} with the id from the submit response until status is completed or failed, then read the URL from output.
Do I need to poll at all? No. A top-level "webhook_url" on the submit body delivers the same job object, signed and retried for about 55 hours.
Read the catalog record, send the key in X-API-Key, and treat the submit response as an id rather than a result.