Workspace API

Your brand's AI-search visibility, as data. The Workspace API gives you programmatic, read-only access to everything your workspace tracks: daily visibility scores across 8 AI engines, the prompt-level answers behind them, every cited source, competitor share of voice, and, when you connect Google Analytics and Search Console, the visits and search demand on the other end of those answers. That data can live where your team already works, whether that is your data warehouse, your BI dashboards, or the tools you build in-house.

Availability. The Workspace API is available on enterprise plans. If you'd like access, or want to talk through an integration before committing, email hello@honeyb.ai. We typically have keys issued and a first sync running within days.

Why build on the Honeyb API

  • Your warehouse, your joins. Sync visibility and citation data into Snowflake, BigQuery, or any store, and join it with your CRM, traffic, and content data to answer questions no single dashboard can.
  • The whole funnel, not just the top. Mentioned, cited, clicked: the traffic endpoints carry the AI-attributed sessions and the search queries behind your pages, so one query can put an answer's citation next to the visits it produced.
  • Numbers that match the product. The API reads the same data with the same counting rules as the Honeyb dashboard. What your team sees in the app is what your pipeline receives.
  • A contract you can depend on. /api/v1 is additive-only and engine identifiers are stable slugs; the Versioning section below is the full promise.
  • Your own data, always. One API key reads exactly one workspace. Keys are stored only as SHA-256 hashes, revocable instantly, and structurally unable to see another customer's data: the scoping is the credential, not a filter.

Quick start

Every request carries your API key as a bearer token:

curl https://app.honeyb.ai/api/v1/visibility \
  -H "Authorization: Bearer <YOUR_API_KEY>"
{
  "workspaceId": "8b1e...",
  "latestVisibilityScore": 42.5,
  "latestTotalMentions": 34,
  "latestTotalPrompts": 80,
  "latestAnalysisAt": "2026-08-03T06:12:04.000Z",
  "peakVisibilityScore": 51.0,
  "peakVisibilityAt": "2026-07-20T06:10:11.000Z",
  "dailyScores": [
    { "date": "2026-08-02", "brandMentions": 31, "totalQueries": 80, "visibilityScore": 38.75 }
  ]
}

All responses are JSON over HTTPS; all timestamps are ISO 8601 UTC. The API is server-to-server by design, with no CORS, so your key never ships to a browser.

Managing your keys

API access is enabled for your workspace as part of your contract. Once it is on, any Owner or Editor of your workspace manages keys directly in the app under Settings → API access, with no support ticket needed:

  • Generate. The key is shown exactly once, so store it in a secrets manager. We keep only a hash, so if a key is lost, revoke it and generate a new one.
  • Rotate. Generate the new key first, then revoke the old one, so your integration never has a gap. Revocation takes effect immediately.
  • If a key leaks, revoke it in the app and it stops working on the next request. Only key hashes are stored on our side, so a database leak yields no usable credentials.

Versioning

/api/v1 is additive-only: fields may be added to responses, but never removed or retyped. Breaking changes ship as /api/v2 with a documented deprecation window. Build your parsers to ignore unknown fields.

Errors and limits

Errors are JSON with a stable machine-readable code:

StatusCodeMeaning
400invalid_queryA query parameter is malformed
401unauthorizedMissing, invalid, revoked, or expired key, or API access is currently disabled for the workspace (your keys are kept and resume working when it is re-enabled)
429rate_limitedOver the request budget; back off and retry
500internal_errorOur side; monitored and alerted on

Standard limit is 1,000 requests per key per hour, comfortably above a daily warehouse sync, and can be raised per contract. Failed authentication attempts are separately throttled per IP.

Pagination

Two schemes, matched to the data:

  • Offset (page + pageSize, max 200 per page) on /prompts, /results and /search-queries. Offset pagination stops at 2,000 rows per filter window; on /results and /search-queries, narrow the since or windowDays window instead of paging further (/prompts has no window and typical prompt sets sit well inside the cap).
  • Keyset (cursor) on /citations, the recommended way to sync incrementally: pass the previous response's nextCursor until it returns null, and persist your last since checkpoint between syncs.

Endpoints

EndpointWhat it returns
GET /api/v1/visibilityVisibility summary + up to 365 days of daily history
GET /api/v1/promptsThe tracked prompt set (topic, intent, search volume)
GET /api/v1/resultsPrompt-level AI answers, one row per prompt × engine × run
GET /api/v1/citationsEvery URL cited in your answers, built for incremental sync
GET /api/v1/competitorsShare of voice per brand observed in your answers
GET /api/v1/web-trafficSessions and conversions with the AI-attributed slice (Google Analytics)
GET /api/v1/search-queriesSearch queries earning impressions and clicks (Search Console)

GET /api/v1/visibility

The workspace's visibility summary: latest score, peak, and up to 365 days of daily history (ascending by date). The quick-start example above shows the full response shape.

Query parameters: none.

visibilityScore is the percentage of tracked prompt runs whose answers mentioned your brand that day. New workspaces return null metrics and an empty dailyScores until the first analysis completes.

GET /api/v1/prompts

The tracked prompt set (active and paused). Internal draft suggestions are never returned.

Query parameters: page (default 1), pageSize (default 50, max 200).

{
  "data": [
    {
      "id": "3f9a...",
      "prompt": "best enterprise billing software",
      "status": "active",
      "topic": "Enterprise Billing",
      "intent": "commercial",
      "searchVolume": 720,
      "tags": [],
      "createdAt": "2026-07-30T09:00:00.000Z"
    }
  ],
  "page": 1,
  "pageSize": 50,
  "total": 100
}

GET /api/v1/results

Prompt-level AI answers: one row per prompt × engine × daily run, newest first, carrying the full answer text, mention counts, competitors named alongside you, and sentiment.

Query parameters: page, pageSize, since (ISO 8601; default: last 90 days).

{
  "data": [
    {
      "id": "b2c4...",
      "promptId": "3f9a...",
      "prompt": "best enterprise billing software",
      "engine": "chatgpt",
      "answer": "For enterprise revenue management, the leading options are...",
      "brandMentions": 1,
      "totalMentions": 5,
      "competitorsMentioned": ["Zuora", "Chargebee"],
      "sentiment": "positive",
      "sentimentConfidence": 82,
      "createdAt": "2026-08-03T06:11:52.000Z"
    }
  ],
  "page": 1,
  "pageSize": 50,
  "total": 812
}

Notes:

  • answer is the engine's own text. It is uncontrolled third-party content that may name individuals or mirror live web content, and is provided for your internal analysis.
  • Rows where the provider call failed are excluded.
  • engine is one of the stable slugs chatgpt, gemini, claude, perplexity, grok, deepseek, google_ai_mode, google_ai_overviews (the set matches your plan's coverage). Slugs never change when we upgrade the underlying models. Rows from before April 2026 can carry legacy model identifiers outside this set (e.g. gpt-4o-mini); treat unrecognized values as additional engines rather than rejecting the row.
  • Rows appear as each engine's daily run completes, so total for the current day grows until the cycle finishes (~07:00 UTC). Sync after that and counts are stable.

GET /api/v1/citations

Every URL cited in your answers; the best endpoint for incremental sync. Cursor-paginated in stable ascending order.

Query parameters: pageSize (default 50, max 200), since (ISO 8601; default: last 90 days), cursor (opaque, from the previous nextCursor).

{
  "data": [
    {
      "id": "d81f...",
      "url": "https://example.com/best-billing-software",
      "domain": "example.com",
      "pageType": "listicle",
      "position": 2,
      "brandMentioned": false,
      "sentiment": null,
      "createdAt": "2026-08-03T06:11:52.000Z"
    }
  ],
  "pageSize": 50,
  "nextCursor": "MTc1NDIw..."
}

createdAt is inherited from the analysis run (not row insert time), so a since checkpoint never misses late-arriving rows. Iterate: request → process data → pass nextCursor back → stop when nextCursor is null.

The default 90-day window applies to the first page only: once you're paging with a cursor, the cursor bounds the scan, so a slow sync can never skip rows at the window edge. An explicit since is always honoured.

Within one cursor walk, every row is delivered exactly once. Across syncs, checkpoint the createdAt of the last row you processed and pass it as the next sync's since: the boundary is inclusive, so rows sharing that exact timestamp are re-delivered rather than skipped. De-duplicate on id and a nightly sync can neither skip nor double-count a citation.

GET /api/v1/competitors

Share of voice across every brand observed in your own tracked answers, computed with the same counting rules as your dashboard.

Query parameters: windowDays (default 30, max 365).

{
  "workspaceId": "8b1e...",
  "windowDays": 30,
  "scannedResults": 1840,
  "isTruncated": false,
  "totalBrandMentions": 412,
  "competitors": [
    { "brand": "Zuora", "mentions": 118, "shareOfVoicePercent": 28.64, "isWorkspaceBrand": false },
    {
      "brand": "BillingPlatform",
      "mentions": 96,
      "shareOfVoicePercent": 23.3,
      "isWorkspaceBrand": true
    }
  ]
}

Up to the 50 most-mentioned brands are returned, and your own brand is always among them (with 0 mentions if the engines never mentioned it). Only brands appearing in your workspace's own answers are reported; this endpoint never exposes other customers' data.

The calculation covers the most recent 2,000 answers in the window (scannedResults says how many were used). If the window matched more than that, isTruncated is true and the shares reflect the most recent slice. Narrow windowDays until isTruncated is false for full-window coverage.

GET /api/v1/web-traffic

Sessions and conversions from your Google Analytics 4 property over the window, with the slice attributed to AI assistants and the landing pages and referrers behind it. This is the "clicked" half of the funnel: pair it with /citations to see which cited pages actually receive AI-driven visits.

Requires a Google Analytics property connected in the app under Web traffic. Until one is connected, the endpoint answers with totals: null.

Query parameters: windowDays (default 30, max 90).

{
  "workspaceId": "8b1e...",
  "windowDays": 30,
  "analytics": { "connected": true, "latestDataDate": "2026-08-24" },
  "totals": {
    "sessions": 48210,
    "conversions": 612,
    "aiSessions": 1840,
    "aiConversions": 47,
    "aiSharePercent": 3.8
  },
  "topAiLandingPages": [
    { "landingPage": "/pricing", "sessions": 412, "conversions": 19 }
  ],
  "topAiSources": [{ "source": "chatgpt.com", "sessions": 980 }]
}

Notes:

  • totals is null, never zeroes, whenever nothing was measured in the window, and analytics tells you which case you are in: connected: false means no property is connected; connected: true with a null latestDataDate means the first sync has not landed; a latestDataDate older than the window means the sync has stopped running (a revoked Google authorisation is the usual cause) and the window went unmeasured. An empty response is "not measured", never "traffic fell to zero".
  • aiSessions is a floor, not a census. Attribution reads the referrer and several assistants strip it, so real AI-driven traffic is at least this number. Report it as "at least", the way the dashboard does.
  • latestDataDate is your staleness check. Traffic is re-synced nightly, so a date more than a day or two behind means the sync has not caught up and the window is partial. If it falls behind the whole window, totals goes back to null rather than reporting the zero that partial data would imply. Reconnect the property in the app if it stays stale.
  • windowDays is capped at 90 because 90 days is the window we keep synced. A larger value comes back clamped, with the clamped number echoed in windowDays.

GET /api/v1/search-queries

Every Search Console query that earned impressions for your site in the window, grouped across days and pages, ordered by impressions descending. This is the demand signal behind AI answers: isPromptLike marks question-shaped queries that map to prompts, isLongTail marks phrasing of four words or more.

Requires a Search Console property connected in the app under Web traffic.

Query parameters: windowDays (default 30, max 90), page (default 1), pageSize (default 50, max 200).

{
  "workspaceId": "8b1e...",
  "windowDays": 30,
  "searchConsole": { "connected": true, "latestDataDate": "2026-08-24" },
  "data": [
    {
      "query": "best billing software for saas",
      "impressions": 3120,
      "clicks": 74,
      "ctrPercent": 2.4,
      "avgPosition": 8.4,
      "isPromptLike": false,
      "isLongTail": true
    }
  ],
  "page": 1,
  "pageSize": 50,
  "total": 1840
}

Notes:

  • searchConsole carries the same connection and freshness contract as analytics above, including the stalled-sync case, and an empty data array means "nothing measured", never "no search demand".
  • total counts distinct queries in the window, which is what page walks.
  • avgPosition is weighted by impressions, the way Search Console computes its own average position, so the number matches the one in your Search Console export for the same window. It is null for queries whose rows all predate position collection.
  • Offset paging stops at 2,000 queries per window. Narrow windowDays to bring the set inside the cap; total keeps reporting the full count so a truncated walk is never silent.
  • Queries Google suppresses for privacy never reach Search Console's API, and so never reach this endpoint either.

Typical warehouse sync

# Nightly: pull citations incrementally from a stored checkpoint
curl -s "https://app.honeyb.ai/api/v1/citations?since=$LAST_SYNC&pageSize=200" \
  -H "Authorization: Bearer $HONEYB_API_KEY"
# ...follow nextCursor until null, then store the new checkpoint.

# Nightly: pull yesterday's results
curl -s "https://app.honeyb.ai/api/v1/results?since=$YESTERDAY&pageSize=200" \
  -H "Authorization: Bearer $HONEYB_API_KEY"

# Daily headline metrics
curl -s "https://app.honeyb.ai/api/v1/visibility" \
  -H "Authorization: Bearer $HONEYB_API_KEY"

# Daily: AI-attributed traffic, and the search demand behind it
curl -s "https://app.honeyb.ai/api/v1/web-traffic?windowDays=28" \
  -H "Authorization: Bearer $HONEYB_API_KEY"
curl -s "https://app.honeyb.ai/api/v1/search-queries?windowDays=28&pageSize=200" \
  -H "Authorization: Bearer $HONEYB_API_KEY"

Data freshness and history

Analyses run daily per engine; sync after ~07:00 UTC to capture the full previous cycle. Your history is retained in full for the life of your contract and accumulates from the day tracking starts: the earlier your prompt set is live, the deeper your dataset at every renewal.

The two traffic endpoints are the exception to that unbounded history. Analytics and Search Console data is re-synced nightly as a trailing 90-day window, so /web-traffic and /search-queries serve the last 90 days only. Warehouse the daily pulls yourself if you want traffic history that outlives that window, and use latestDataDate to skip a pull that would land on a sync that has not run yet.

Frequently asked

Can our AI assistants query this directly? An MCP endpoint (for Claude, ChatGPT, Cursor, and other assistants) built on the same data layer is on our near-term roadmap; ask us about early access.

What about exports instead of polling? For warehouse-first teams we offer scheduled exports (NDJSON/CSV to object storage) and can discuss warehouse-native sharing; tell us your stack.

Get access

The API ships under enterprise agreements alongside onboarding support: a prompt-set design session, named-competitor curation, and a shared Slack channel during integration. Email hello@honeyb.ai with a sentence about what you're building and we'll take it from there.