prompter API
Ask one of nine AI platforms a single question and get the raw answer back, with native citations where the platform provides them. prompter is stateless: every call is a real, live upstream request.
Overview
prompter exposes a small REST API. You send a prompt and a platform, prompter calls that platform, and returns the unmodified answer. It never summarizes or invents data: if a platform cannot answer, the response says so.
Base URL
https://api.prompter.searchestra.com
Conventions
- All request and response bodies are
application/json. - Successful reads return a top level
datafield. Errors return a top levelerrorfield (see Errors). - Every
/v1/*endpoint requires theX-API-Keyheader./healthzdoes not. - Timestamps are UTC, ISO 8601 (for example
2026-08-16T13:47:09Z).
4xx the error message is real and specific. On 5xx (500 and 503) the message is always masked to a fixed string; the real cause is only in prompter's server logs.Quickstart
Send your first query. Replace prsk_your_key with a key that carries the run permission (see Authentication).
curl -X POST https://api.prompter.searchestra.com/v1/collect \
-H "X-API-Key: prsk_your_key" \
-H "Content-Type: application/json" \
-d '{
"platform": "perplexity",
"promptText": "which dishwasher detergent is best, briefly",
"countryCode": "tr",
"language": "tr"
}'{
"data": {
"rawText": "Finish and Fairy are among the most preferred...",
"rawHtml": "",
"citations": [
{ "url": "https://example.com/review", "title": "Best detergent", "position": 1 }
],
"textLinks": [],
"collectedAt": "2026-08-16T13:47:09Z"
}
}Authentication
Every /v1/* request must carry an API key in the X-API-Key header. There are two kinds of key, each with its own prefix.
| Prefix | Type | Bound to | Use for |
|---|---|---|---|
prsk_ | service | a resource (tenant, workspace or project) | machine-to-machine access. Survives even if the person who created it leaves. |
pruk_ | user | a person's membership | scripts a person runs as themselves. Stops working the moment that person loses access. |
X-API-Key: prsk_your_key
Scope
Every key is scoped to exactly one resource: a tenant, a workspace or a project. The key can only act within that resource. A project key cannot touch another project; a workspace key cannot reach outside its workspace.
Permissions
Each key carries an explicit permission set. Three permissions exist today; each endpoint states which one it needs.
| Permission | Grants |
|---|---|
run | Execute a live query (POST /v1/collect). A key without it gets 403 forbidden. |
read | Read stored resources, such as listing projects. |
manage | Create, edit and delete projects within the key's scope. |
For a user key, permissions are additionally capped by that person's role in the scope. Granting manage to a viewer still yields a key that cannot manage, and run to a viewer yields a key that cannot run.
The server stores only a SHA-256 hash of the key. The raw value is shown once, at creation, and cannot be recovered. If a key is lost, issue a new one. A resource can hold several keys; revoking one does not affect the others. Keys may also carry an optional expiry.
401 unauthorized; a key without the needed permission returns 403 forbidden.Query a platform
The one-call shortcut: ask a single platform one question, under a project, and get the stored run back. Internally it saves the prompt (reusing an existing one with the same text in the project) and runs it, so the answer is persisted with its real cost, attributed to the project. It is the convenience wrapper over save a prompt then run it.
projectId); a tenant/workspace key or a panel session must send projectId, and the key's scope must cover it (out of scope → 404). Needs the run permission. Country and language come from the project, not the request.
Request body
| Field | Type | Notes |
|---|---|---|
| platform required | string | One of the nine codes (see Platforms). |
| promptText required | string | The question to ask. Trimmed; must not be empty. |
| projectId conditional | string | Required unless the key is project-scoped. The project the run is stored under; the key's scope must cover it. |
curl -X POST https://api.prompter.searchestra.com/v1/collect \
-H "X-API-Key: prsk_your_key" \
-H "Content-Type: application/json" \
-d '{ "platform": "perplexity", "promptText": "which dishwasher detergent is best" }'Response data (a single run)
Returns one stored run: id, promptId, projectId, platform, status (ok/error), answerText, citations, textLinks, and the real cost fields (costEstimate, model, inputTokens, outputTokens).
{
"data": {
"id": "run_…", "promptId": "prm_…", "projectId": "prj_…", "platform": "perplexity",
"status": "ok", "answerText": "Finish and Fairy are among the most preferred…",
"citations": [ { "url": "https://example.com/review", "title": "Best detergent", "position": 1 } ],
"textLinks": [], "costEstimate": 0.0041, "model": "sonar", "createdAt": "2026-08-31T13:47:09Z"
}
}citations are the provider's native sources. textLinks are links the model wrote into its prose. They use different ordering systems, so relate them by URL, not by position.List platforms
Returns the fixed list of registered platforms. A platform whose credentials are not configured still appears here; it only fails on a real /v1/collect call. Fetch this once and cache it on your side.
{
"data": [
{ "code": "chatgpt", "unsupportedCountries": [] },
{ "code": "perplexity", "unsupportedCountries": null }
]
}unsupportedCountries is either an empty array or null; both mean "no restriction". Treat them the same way.
Health check
Liveness probe for container orchestration. No authentication. Returns 200 when the service is up.
Projects
A project pairs your brand with a market (country and language); prompts and runs live under it. These endpoints let a machine key manage projects, so you can provision brands from your own systems. They accept the same panel session (JWT) or an API key.
404 not_found (the resource is hidden, not merely refused).List projects
Lists the projects in a workspace. Needs the read permission and a key whose scope covers the workspace (tenant or that workspace).
{
"data": [
{ "id": "prj_…", "workspaceId": "wsp_…", "name": "Acme", "slug": "acme", "countryCode": "tr", "language": "tr" }
]
}Create a project
Creates a project under a workspace. Needs the manage permission and a tenant- or workspace-scoped key (a project-scoped key cannot create, and gets 403 forbidden).
Request body
| Field | Type | Notes |
|---|---|---|
| name required | string | Display name. The slug is derived from it and made unique in the workspace. |
| countryCode required | string | The market the brand is measured in. One of tr, us, gb, de, fr, es, in (uk is accepted as gb). There is no default country. |
| language required | string | Language code, for example tr, en or pt-br. aimode and aio query Google in this language. |
curl -X POST https://api.prompter.searchestra.com/v1/workspaces/wsp_123/projects \
-H "X-API-Key: prsk_your_key" \
-H "Content-Type: application/json" \
-d '{ "name": "Acme", "countryCode": "tr", "language": "tr" }'{
"data": { "id": "prj_…", "workspaceId": "wsp_123", "name": "Acme", "slug": "acme", "countryCode": "tr", "language": "tr" }
}Update a project
Renames a project or changes its market. Needs the manage permission and a key whose scope covers the project (tenant, its workspace, or that project). Same body fields as create, all optional: send only what changes, omitted fields keep their value. A field you send is validated like on create, so an empty string is rejected rather than clearing the field.
The market (countryCode and language) is locked once the project has its first run, so earlier results stay tied to one market. Changing it after that returns 409 conflict; to measure another market, create a new project.
Delete a project
Deletes a project and everything under it (prompts, runs and scoped keys cascade). Needs the manage permission and a key whose scope covers the project. This cannot be undone.
Prompts and runs
These endpoints are the persisted run channel: you save a prompt under a project, run it across platforms, and the answer is stored with its real cost, attributed to the project. This is the same surface the panel uses, so a machine key does exactly what a person does in the app. It is the counterpart to /v1/collect, which is stateless and records nothing.
read, running needs run, creating or deleting a prompt needs manage. Out of scope → 404; missing permission → 403.List prompts
Lists the saved prompts in a project. Needs read.
Create a prompt
Saves a prompt under a project. Needs manage.
| Field | Type | Notes |
|---|---|---|
| promptText required | string | The question to store. Trimmed; must not be empty. |
{ "data": { "id": "prm_…", "projectId": "prj_…", "text": "best crm for startups", "createdAt": "2026-08-31T12:00:00Z" } }Run a prompt
Runs a saved prompt across the chosen platforms as live upstream requests. The project (and its country/language) is taken from the prompt. Each platform is independent: one failing does not stop the others. Every result is stored as a run with its real cost. Needs run.
| Field | Type | Notes |
|---|---|---|
| platforms required | string[] | One or more platform codes (see Platforms). Deduplicated; at least one. |
curl -X POST https://api.prompter.searchestra.com/v1/prompts/prm_123/run \
-H "X-API-Key: prsk_your_key" \
-H "Content-Type: application/json" \
-d '{ "platforms": ["chatgpt", "perplexity"] }'{
"data": [
{ "id": "run_…", "promptId": "prm_123", "platform": "chatgpt", "status": "ok",
"answerText": "…", "citations": [], "textLinks": [],
"costEstimate": 0.0037, "model": "gpt-4o", "inputTokens": 812, "outputTokens": 143,
"createdAt": "2026-08-31T12:00:05Z" }
]
}status is ok or error per platform (partial success). A failed platform carries errorMessage and zero cost.
List a prompt's runs
Returns the run history for a prompt, newest first. Needs read.
Project usage
Aggregates the project's real cost from stored runs, total and per platform. Needs read. (GET /v1/projects/{projectId}/overview returns the same usage plus the prompt count and recent runs.)
{
"data": { "totalRuns": 42, "totalCostUsd": 0.1873,
"byPlatform": [ { "platform": "chatgpt", "runs": 20, "costUsd": 0.09 } ] }
}Platforms
Nine platforms have a real integration. citations are native only on the three sourced platforms; the six LLM platforms surface links through textLinks instead.
| Code | Provider | Citations |
|---|---|---|
chatgpt | OpenAI Chat Completions | textLinks only |
claude | Anthropic Messages | textLinks only |
gemini | Google Generative Language | textLinks only |
deepseek | DeepSeek Chat Completions | textLinks only |
grok | xAI Chat Completions | textLinks only |
metaai | Meta Model API | textLinks only |
perplexity | Perplexity Sonar | native |
aimode | Google AI Mode (DataForSEO) | native |
aio | Google AI Overviews (DataForSEO) | native |
Errors
Errors return an error object with a code and a message.
{ "error": { "code": "unauthorized", "message": "X-API-Key required." } }| Status | code | When |
|---|---|---|
| 400 | validation_failed | Empty promptText, or the country is not supported. |
| 401 | unauthorized | Missing, invalid, revoked or expired API key. |
| 403 | forbidden | The key is valid but lacks the permission this call needs (run, read or manage), or is a project-scoped key attempting a workspace-level action. |
| 404 | not_found | Unknown platform code, or a workspace/project outside the key's scope (hidden rather than refused). |
| 422 | quota_exceeded | Daily request quota or cost budget for this tenant and platform is spent. Try again tomorrow. |
| 429 | rate_limited | Per-key requests per minute or concurrency limit exceeded. Retry shortly. |
| 500 | internal | Upstream or internal failure. Message is masked. |
| 503 | unavailable | Platform not configured, or its circuit breaker is open. Message is masked. |
422 means "try again tomorrow" (a daily budget is spent). 429 means "try again shortly" (a short window limit). Do not confuse them.Rate limits and quotas
prompter applies five independent guards. The rate and concurrency guards apply only to the run endpoints (POST /v1/collect and POST /v1/prompts/{promptId}/run) that spend real money; listing, creating and reading are exempt.
| Guard | Scope | On limit |
|---|---|---|
| Requests per minute | per identity (API key, or panel user) | 429 rate_limited |
| Concurrent requests | per identity (API key, or panel user) | 429 rate_limited |
| Daily request quota | per tenant and platform | 422 quota_exceeded |
| Daily cost budget | per tenant and platform | 422 quota_exceeded |
| Circuit breaker | per platform, all tenants | 503 unavailable |
The rate and concurrency guards are keyed by identity: a machine call by its API key, a panel call by its user. One key being throttled does not affect the tenant's other keys or users. Limits scale with your plan. The circuit breaker opens after a platform fails repeatedly and closes automatically after a cooldown.