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.
| Endpoint | Description |
|---|---|
| POST /v1/agent | Submit a research question. Set preset (fast, low, medium, high, xhigh) or model. Always returns status: "queued" with the resp_… id. |
| GET /v1/agent/:id | Poll 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/cancel | Cancel a running response. Returns {"response_id": "…", "status": "cancelling"}. Poll for the terminal cancelled status. |
| POST /v1/responses | Alias for /v1/agent (OpenAI SDK compatibility). |
| GET /v1/responses/:id | Alias for /v1/agent/:id. |
| POST /v1/responses/:id/cancel | Alias 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
| Preset | Research grade | Credits |
|---|---|---|
| fast | Focused (up to 10 min) | 1 |
| low | Focused (up to 10 min) | 1 |
| medium | Thorough (up to 30 min) | 2 |
| high | Thorough (up to 30 min) | 2 |
| xhigh | Comprehensive (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"
}
}| HTTP | type | When |
|---|---|---|
| 400 | invalid_request_error | Validation (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) |
| 401 | invalid_request_error | Missing or invalid Bearer token |
| 404 | invalid_request_error | Unknown response ID or wrong owner |
| 429 | rate_limit_error | Concurrency limit. Includes Retry-After header. |