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.

  1. The catalog record, GET /catalog/apis/{api_id}?expand=inputs, for current availability, request fields, required inputs, enum values, and pricing metadata.
  2. The operation’s llms/... specification, for the exact submit endpoint, request and response examples, polling behavior, uploads, and errors.
  3. llms.txt itself for 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

A three-hop request flow diagram: submit a job, poll it until the status is terminal, then fetch the media URL

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 pathWhat 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}/contentReturns the output URL as { "url": ... }.
DELETE /v1/media/jobs/{request_id}Erases media and prompt; answers 204.

The supporting endpoints

Method and pathWhat it does
GET /catalog/apisLists callable operations; needs no key.
POST /catalog/apis/{api_id}/quotePrices a body without running it.
GET /billing/balanceReads 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.