Pocket Search

API Reference

Perplexity Agent API compatible. The response shapes follow the v1 contract.

Authentication

Send your API key as a Bearer token: Authorization: Bearer $POCKET_SEARCH_KEY

Endpoints

Async-only. Every POST /v1/agent returns status: "queued" immediately, then you poll GET /v1/agent/:id until the status is terminal. A run can use the preset's full budget (10m / 30m / 60m) plus a bounded salvage window. A synchronous create() would outlive your HTTP timeout, and a timed-out sync call strands a paid answer whose resp_ id you never received. background: false is rejected with a 400; omit the field or send true.

EndpointDescription
POST /v1/agentSubmit a research question. Set preset (fast, low, medium, high, xhigh) or model. Always returns status: "queued" with the resp_… id.
GET /v1/agent/:idPoll a response. Completed responses contain the output array with search_results and the Markdown report. Results stay available for 24 hours.
POST /v1/agent/:id/cancelCancel a running response. Returns {"response_id": "…", "status": "cancelling"}. Poll for the terminal cancelled status.
POST /v1/responsesAlias for /v1/agent (OpenAI SDK compatibility).
GET /v1/responses/:idAlias for /v1/agent/:id.
POST /v1/responses/:id/cancelAlias for /v1/agent/:id/cancel.

SDK setup

Both SDKs hit /v1/responses on our server, but the baseURL differs because of how each SDK constructs paths.

Perplexity SDK

// No /v1 suffix. The SDK bakes
// /v1 into its request paths
const client = new Perplexity({
  apiKey: POCKET_SEARCH_KEY,
  baseURL: "https://pocketsearch.ai",
});

OpenAI SDK

// Include /v1. The SDK appends
// /responses to the baseURL
const client = new OpenAI({
  apiKey: POCKET_SEARCH_KEY,
  baseURL: "https://pocketsearch.ai/v1",
});

Always poll. Both SDKs' default create() call sends no background field. On our server that means a background run: you get resp_… back immediately and must poll for the result. Research can run the full preset budget, which exceeds both SDKs' default HTTP timeouts, so don't set a short client timeout and wait on create(). Submit, then poll retrieve().

Presets

PresetResearch gradeCredits
fastFocused (up to 10 min)1
lowFocused (up to 10 min)1
mediumThorough (up to 30 min)2
highThorough (up to 30 min)2
xhighComprehensive (up to 60 min)4

Failed runs refund their reserved credits automatically.

Response statuses

  • queued
  • in_progress
  • completed
  • failed
  • incomplete
  • cancelled

Non-terminal: queued, in_progress. Terminal: completed, failed, incomplete, cancelled.

Examples

Submit a research question

curl -X POST https://pocketsearch.ai/v1/agent \
  -H "Authorization: Bearer $POCKET_SEARCH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": "State of solid state battery manufacturing, 2026",
    "preset": "high"
  }'

# 200. Always immediate, always "queued" (async-only)
{
  "id": "resp_7f3d…",
  "object": "response",
  "created_at": 1786767120,
  "status": "queued",
  "output": []
}

Poll for result

# The run can take up to the preset's budget (high = 30m).
# Poll until terminal.
while true; do
  curl -s https://pocketsearch.ai/v1/agent/resp_7f3d… \
    -H "Authorization: Bearer $POCKET_SEARCH_KEY" \
    | grep -q '"status": "\(completed\|failed\|incomplete\|cancelled\)"' \
    && break
  sleep 15
done

# 200 when status is terminal, then read .output

SDK create + poll (the pattern that works)

// Do NOT await create() to completion. It returns queued immediately, and the run
// outlives both SDKs' default HTTP timeouts. Submit, then poll retrieve().
const created = await client.responses.create({
  model: "zai/glm-5.1",
  input: "Solid state battery manufacturing, 2026",
  preset: "high",
});

let resp = created;
while (!["completed", "failed", "incomplete", "cancelled"].includes(resp.status)) {
  await new Promise((r) => setTimeout(r, 15000));
  resp = await client.responses.retrieve(created.id);
}

console.log(resp.output_text);   // surfaced on both SDKs (see "Read the answer text")

Completed response

{
  "id": "resp_7f3d…",
  "object": "response",
  "created_at": 1786767120,
  "completed_at": 1786768365,
  "model": "default",
  "status": "completed",
  "output_text": "# State of Solid State Battery Manufacturing\n\n…",
  "output": [
    {
      "type": "search_results",
      "queries": ["solid state battery manufacturing 2026", "…"],
      "results": [
        { "id": 1, "title": "…", "url": "https://…", "source": "web" },
        { "id": 2, "title": "…", "url": "https://…", "source": "web" }
      ]
    },
    {
      "type": "message",
      "id": "msg_a1b2…",
      "role": "assistant",
      "status": "completed",
      "content": [
        {
          "type": "output_text",
          "text": "# State of Solid State Battery Manufacturing\n\nToyota plans…",
          "annotations": [
            {
              "type": "url_citation",
              "url": "https://idtechex.com/…",
              "title": "IDTechEx",
              "start_index": 148,
              "end_index": 213
            }
          ]
        }
      ]
    }
  ],
  "usage": {
    "input_tokens": 0,
    "output_tokens": 0,
    "total_tokens": 0,
    "pocket_search": {
      "mode": "30m",
      "credits_charged": 2,
      "credits_refunded": 0,
      "duration_ms": 1245000,
      "steps": 68,
      "research_receipt": {
        "version": 1,
        "queries": [
          {"text": "solid state battery manufacturing 2026", "results": [{"url": "https://…", "label": "…", "title": "…"}]}
        ],
        "sourceInvestigations": [{"label": "Toyota production timeline", "sources": [{"url": "https://…", "label": "…"}]}]
      }
    }
  },
  "error": null
}

Cancel a running response

curl -X POST https://pocketsearch.ai/v1/agent/resp_7f3d…/cancel \
  -H "Authorization: Bearer $POCKET_SEARCH_KEY"

# 200
{ "response_id": "resp_7f3d…", "status": "cancelling" }

Read the answer text

// Both SDKs surface output_text on the poll. OpenAI synthesizes it; the
// Perplexity SDK's retrieve() is a passthrough, so we ship it ourselves.
const res = await client.responses
  .retrieve("resp_7f3d…");

console.log(res.output_text);
// "# State of Solid State Battery…"

Errors

Errors use the OpenAI envelope:

{
  "error": {
    "code": null,
    "message": "input is required",
    "type": "invalid_request_error"
  }
}
HTTPtypeWhen
400invalid_request_errorValidation (background: false, malformed fields, unknown preset), cancel on terminal, unresolvable previous_response_id, blocked content (the pre-run content screen; the category rides in param)
401invalid_request_errorMissing or invalid Bearer token
404invalid_request_errorUnknown response ID or wrong owner
429rate_limit_errorConcurrency limit. Includes Retry-After header.