REST API Reference
The Fond REST API lets you retrieve analytics data and trigger runs programmatically. All endpoints live under /api/v1/.
Authentication
Every request must include an API key. You can create keys from the dashboard at Settings > API Keys (admin/owner role required).
Pass the key in either header:
Authorization: Bearer sk-your-key-here
X-API-Key: sk-your-key-here
Keys are shown once at creation. Store them securely — they cannot be retrieved later.
Error responses
All auth failures return 401:
{ "error": "Invalid API key" }
This applies to missing, invalid, revoked, and expired keys.
Endpoints
List websites
GET /api/v1/websites
Returns all websites with setup status COMPLETE in the organization.
Response
{
"websites": [
{
"id": "clx...",
"url": "https://example.com",
"name": "Example Store",
"createdAt": "2026-01-15T10:30:00.000Z"
}
]
}
List runs
GET /api/v1/websites/:websiteId/runs
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
status | string | — | Filter by status: PENDING, RUNNING, COMPLETED, COMPLETED_WITH_ERRORS, FAILED |
limit | integer | 20 | Results per page (1-100) |
offset | integer | 0 | Number of results to skip |
Response
{
"runs": [
{
"id": "clx...",
"runDate": "2026-03-06T00:00:00.000Z",
"status": "COMPLETED",
"triggeredBy": "MANUAL",
"totalQueries": 24,
"totalResults": 72,
"failedResults": 0,
"locale": "en-US",
"startedAt": "2026-03-06T12:00:00.000Z",
"completedAt": "2026-03-06T12:05:30.000Z",
"createdAt": "2026-03-06T12:00:00.000Z"
}
],
"total": 42,
"limit": 20,
"offset": 0
}
Trigger a run
POST /api/v1/websites/:websiteId/runs
Starts a new query run for the website. Returns 409 if a run is already in progress.
Request body (optional, JSON)
| Field | Type | Description |
|---|---|---|
name | string | Optional label for the run (1-100 chars) |
queryIds | string[] | Run only specific queries. Omit to run all. |
Example
curl -X POST https://your-domain.com/api/v1/websites/clx.../runs \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{"name": "Weekly check"}'
Response 201 Created
{
"id": "clx...",
"status": "RUNNING",
"triggeredBy": "MANUAL",
"totalQueries": 24,
"totalResults": 0,
"failedResults": 0,
"startedAt": "2026-03-06T14:00:00.000Z",
"completedAt": null
}
Error responses
| Status | Reason |
|---|---|
400 | Invalid body or no valid queries to run |
404 | Website not found or not in your organization |
409 | A run is already in progress |
Get run details
GET /api/v1/websites/:websiteId/runs/:runId
Returns a single run with summary statistics.
Response
{
"id": "clx...",
"runDate": "2026-03-06T00:00:00.000Z",
"status": "COMPLETED",
"triggeredBy": "MANUAL",
"totalQueries": 24,
"totalResults": 72,
"failedResults": 0,
"locale": "en-US",
"modelVersions": { "gemini": "gemini-2.0-flash", "openai": "gpt-5-mini" },
"repetitions": 3,
"startedAt": "2026-03-06T12:00:00.000Z",
"completedAt": "2026-03-06T12:05:30.000Z",
"errorMessage": null,
"createdAt": "2026-03-06T12:00:00.000Z",
"summary": {
"totalResults": 72,
"avgVisibilityScore": 34.5,
"mentionCount": 45,
"citationCount": 12
}
}
List results
GET /api/v1/websites/:websiteId/runs/:runId/results
Returns paginated query results for a run.
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
provider | string | — | Filter by provider (e.g. gemini, openai) |
queryId | string | — | Filter by specific query |
limit | integer | 50 | Results per page (1-100) |
offset | integer | 0 | Number of results to skip |
Response
{
"results": [
{
"id": "clx...",
"queryId": "clx...",
"provider": "gemini",
"model": "gemini-2.0-flash",
"isMentioned": true,
"isCited": false,
"isLinked": false,
"mentionPosition": 2,
"mentionContext": "...recommended by Example Store...",
"sentiment": "POSITIVE",
"extractedUrls": [],
"citations": null,
"visibilityScore": 45,
"followUpQuestions": ["What products does Example Store offer?"],
"brandInFollowUp": true,
"confidence": 0.85,
"groundingUrls": ["https://example.com/products"],
"errorMessage": null,
"createdAt": "2026-03-06T12:01:00.000Z",
"query": {
"text": "best online stores for electronics",
"queryType": "CATEGORY",
"categoryTier": "SAFETY",
"queryFocus": "PRODUCT"
}
}
],
"total": 72,
"limit": 50,
"offset": 0
}
Trigger an opportunity (WIP)
Internal/WIP — not yet stable. Used by the GEO Content Engine to ingest external opportunity triggers (e.g. matched social posts) and run the content pipeline against them. Subject to change; not recommended for customer integration yet.
POST /api/v1/opportunities
Authenticated with the same API key. Body must include websiteId, url, platform, and context; matchedKeywords is optional (defaults to []). Returns 202 { id } once the trigger is queued; the content pipeline runs asynchronously.
Pagination
All list endpoints support limit and offset parameters and return a total count. To iterate through all results:
# Page 1
GET /api/v1/websites/:id/runs?limit=20&offset=0
# Page 2
GET /api/v1/websites/:id/runs?limit=20&offset=20
Rate limits
There are currently no enforced rate limits on the API. Be considerate with request frequency — avoid tight polling loops when checking run status.
Common workflows
Poll for run completion
# 1. Trigger a run
RUN_ID=$(curl -s -X POST .../runs -H "Authorization: Bearer $KEY" | jq -r .id)
# 2. Poll status every 30 seconds
while true; do
STATUS=$(curl -s .../runs/$RUN_ID -H "Authorization: Bearer $KEY" | jq -r .status)
echo "Status: $STATUS"
[ "$STATUS" = "COMPLETED" ] || [ "$STATUS" = "FAILED" ] && break
sleep 30
done
# 3. Fetch results
curl -s .../runs/$RUN_ID/results -H "Authorization: Bearer $KEY" | jq .
Export all results for a website
OFFSET=0
while true; do
RESPONSE=$(curl -s ".../runs/$RUN_ID/results?limit=100&offset=$OFFSET" \
-H "Authorization: Bearer $KEY")
echo "$RESPONSE" >> results.json
TOTAL=$(echo "$RESPONSE" | jq .total)
OFFSET=$((OFFSET + 100))
[ $OFFSET -ge $TOTAL ] && break
done