Overview
All API routes live under /v1. Successful responses wrap payloads in { "data": … }. Errors return { "errors": [{ status, title, code }] }.
There are three access levels:
- API key: search, answer, and index from your app or server
- Cognito JWT: dashboard management (projects, keys, jobs, analytics)
- Unauthenticated: demo leads, claim, signup, and signin
Authentication
Send your API key as x-surge-api-key: <key> or Authorization: Bearer <key>.
| Prefix | Use | Endpoints |
|---|
pk_live_… | Browser, mobile, React SDK | /v1/search, /v1/answer |
sk_live_… | Server-side only | All public endpoints including /v1/index |
Publishable keys are rate-limited per IP (60 search / 20 answer requests per minute). Secret keys are subject to your plan quota only.
Management routes require a Cognito ID token: Authorization: Bearer <idToken>. The JWT must include custom:tenantId matching the tenantId in the path.
Search
POST/v1/search
API key (pk_ or sk_)
Semantic search over indexed pages.
Request body
{
"query": "pricing plans",
"projectId": "optional",
"limit": 10
}
Response 200
{
"data": {
"query": "pricing plans",
"items": [
{
"url": "https://example.com/pricing",
"title": "Pricing & plans",
"text": "…",
"score": 0.84
}
]
}
}
Answer
POST/v1/answer
API key (pk_ or sk_)
AI-generated answer with cited sources from your indexed content.
Request body
{
"question": "How long is the free trial?",
"projectId": "optional",
"mode": "default"
}
mode is "default" or "fact_check" (stricter grounding).
Response 200
{
"data": {
"question": "How long is the free trial?",
"answer": "The free trial lasts 14 days… [1]",
"sources": [
{ "url": "…", "title": "Pricing", "excerpt": "…" }
]
}
}
Index
POST/v1/index
Secret API key (sk_) only
Queue a crawl and vector index for a URL. Creates a project if needed.
Request body
{
"url": "https://example.com",
"projectId": "optional"
}
Response 202
{
"data": {
"jobId": "01J…",
"projectId": "01J…",
"status": "queued"
}
}
Projects
Manage indexed sites for your tenant. All routes require a Cognito JWT.
GET/v1/tenants/{tenantId}/projects
Cognito JWT
List all projects.
{
"data": [
{
"projectId": "01J…",
"name": "example.com",
"url": "https://example.com",
"indexStatus": "idle",
"pageCount": 42,
"lastIndexedAt": "2026-05-28T12:00:00.000Z"
}
]
}
POST/v1/tenants/{tenantId}/projects
Cognito JWT
{ "url": "https://example.com", "name": "optional" }
Returns 201 with the created project.
POST/v1/tenants/{tenantId}/projects/{projectId}/reindex
Cognito JWT
Queue a full re-index. Returns 202 with jobId.
DELETE/v1/tenants/{tenantId}/projects/{projectId}
Cognito JWT
Delete project and purge vectors. Idempotent if already removed.
Jobs
GET/v1/tenants/{tenantId}/jobs
Cognito JWT
List recent index jobs.
GET/v1/tenants/{tenantId}/jobs/{jobId}
Cognito JWT
Get a single job. Status: queued, running, completed, or failed.
{
"data": {
"jobId": "01J…",
"projectId": "01J…",
"url": "https://example.com",
"status": "completed",
"pagesIndexed": 120,
"createdAt": "…",
"completedAt": "…"
}
}
API keys
GET/v1/tenants/{tenantId}/api-keys
Cognito JWT
List keys (prefix only; raw secret is never returned again).
POST/v1/tenants/{tenantId}/api-keys
Cognito JWT
{
"name": "Production",
"type": "publishable"
}
type is "publishable" or "secret". Response 201 includes the full key once. Store it securely.
DELETE/v1/tenants/{tenantId}/api-keys/{apiKeyId}
Cognito JWT
Revoke a key immediately.
Analytics
GET/v1/tenants/{tenantId}/analytics/queries
Cognito JWT
Query analytics from the last ~500 searches and answers.
{
"data": {
"totalQueries": 42,
"zeroResultRate": 0.12,
"topQueries": [
{ "query": "pricing", "count": 5, "zeroResults": 0 }
],
"zeroResultQueries": [
{ "query": "enterprise sso", "count": 3, "zeroResults": 3 }
]
}
}
Usage
GET/v1/tenants/{tenantId}/usage
Cognito JWT
Current billing period usage and plan limits.
{
"data": {
"period": "2026-05",
"plan": "starter",
"usage": { "queries": 120, "answers": 30, "pagesIndexed": 450 },
"limits": {
"maxQueriesPerMonth": 1000,
"maxIndexedPages": 1000,
"maxSites": 3
}
}
}
Billing: POST …/billing/checkout with { "plan": "starter" | "plus" | "scale" } returns a Stripe Checkout URL. POST …/billing/portal opens the customer portal.
Errors
| Status | Code | Meaning |
|---|
| 400 | bad_request | Invalid request body or parameters |
| 401 | unauthorized | Missing or invalid API key / JWT |
| 403 | forbidden | Wrong key type, suspended tenant, or wrong tenant |
| 404 | not_found | Resource does not exist |
| 409 | conflict | Duplicate (e.g. email already registered) |
| 429 | quota_exceeded | Rate limit or plan quota exceeded |
| 500 | internal | Server error |