Quorum Data API

Store-visit measurement and location data for agencies and their advertisers. Send Quorum your advertisers and store lists, then read back each store's status, its visit records, and the numbers on your published Quorum reports.

Base URL: https://api.quorum.live    Auth: Bearer token    Swagger UI · ReDoc · openapi.json

Authentication

Send your token in the Authorization header on every request:

Authorization: Bearer <your-token>

Three routes need no token: the health check (GET /v1/health, also served at /health), GET /v1/library/categories and POST /v1/library/audience-intel.

  • A missing or unknown token gets 401.
  • A token can have an expiry date. After it, the token gets 401 with "API token expired". Ask Quorum for a new one.
  • A token can be read-only. A read-only token can call every GET route it is allowed, but POST /v1/intake/* returns 403 with a message saying the token is read-only.
  • Tokens are bound to one agency and stored as SHA-256 hashes, so Quorum cannot show you a token again. Keep it in a secrets manager.

Two kinds of advertiser id

IdWhere it comes fromWhich routes take it
Your DSP advertiser idYour own id (for MNTN, the ${aid} pixel macro). The same value you send as client_advertiser_id to intake./v1/advertisers/{advertiser_id}/locations, .../locations/{segment_md5}/visits, /report, /reports, and every /v1/intake/* route.
Quorum advertiser idListed by GET /v1/advertisers, and returned as quorum_advertiser_id by GET /v1/intake/advertisers once mapped.Every other advertiser_id: the roll-ups (/summary, /campaigns, ...), /visitor-geography, /nearby-competitors, /v1/performance/*, /v1/segments/* and /v1/partner-feed/*. /locations, /visits, /report and /reports also accept a Quorum id your agency owns.

If you have no DSP mapping with Quorum (for example your DSP ids are on a platform Quorum has not crosswalked), use the Quorum ids from GET /v1/advertisers everywhere. When an id is both one of your DSP ids and a Quorum id, the DSP reading wins.

One DSP id can map to more than one Quorum account (for example one per market). /locations then returns the union of their locations, and /visits serves any location configured on any of them.

Quick start

curl https://api.quorum.live/v1/health
# {"status":"ok","snowflake":"connected"}
curl https://api.quorum.live/v1/advertisers \
  -H "Authorization: Bearer <token>"
curl "https://api.quorum.live/v1/advertisers/12345/locations?location_type=POI" \
  -H "Authorization: Bearer <token>"

Access and scope

A token issued to anyone outside Quorum is a partner token. Partner tokens follow the rules below. Internal Quorum tokens are not affected.

1. You read only your own agency's advertisers

  • On the advertiser roll-ups (/v1/advertisers/{advertiser_id}/config, /tab-availability, /summary, /timeseries, /campaigns, /lineitems, /lift, /store-visits/brands), an advertiser that is not your agency's gets 403 with "Advertiser N is not in your agency." Ownership is checked against both of Quorum's advertiser registries. An id that both registries assign to different agencies is refused until Quorum resolves it.
  • Routes that take your DSP id (/locations, /visits, /report, /reports) resolve it inside your agency only, or accept a Quorum id your agency owns. Any other id gets 404.
  • Routes that filter by your agency (/v1/performance/*, /v1/segments/catalog, /v1/advertisers/{advertiser_id}/segments, /v1/partner-feed/*, /visitor-geography, /nearby-competitors) return an empty list or 404 for an advertiser that is not yours.
  • A token can also be limited to a list of advertisers. Any other advertiser_id gets 403.
  • If Quorum cannot check ownership at that moment, you get 503, never data. Retry.

2. No device- or household-level routes

These routes return a 403 to partner tokens. The message points to the Quorum MCP at https://mcp.quorum.live/mcp, which answers the same questions as k-anonymous aggregates:

RouteWhy
POST /v1/resolveReturns a household and its devices.
GET /v1/household/{household_id}/geoHousehold-level.
GET /v1/household/{household_id}/footprintHousehold-level.
GET /v1/household/{household_id}/visitsHousehold-level.
GET /v1/household/{household_id}/publishersHousehold-level.
GET /v1/segments/{segment_md5}/visitsDevice-level rows.
GET /v1/segments/{segment_md5}/visitor-geographySmall-area geography with no privacy floor.
GET /v1/locations/{segment_md5}/visitor-geographySmall-area geography with no privacy floor.
GET /v1/advertisers/{advertiser_id}/segments/{segment_md5}/householdsHousehold list.
GET /v1/performance/householdsOne row per household.

3. Writes need a write-enabled token

POST /v1/intake/* (advertisers, locations, disconnect, reconnect) returns 403 for a read-only token. The qurum_ag_id (or agency_id) in an intake body must equal your token's agency, or the request gets 403.

4. Disconnected advertisers

After you disconnect an advertiser (POST /v1/intake/advertisers/{client_advertiser_id}/disconnect), every read of it returns 410, by your id or by its Quorum advertiser id. Nothing is deleted and capture continues; reconnecting restores everything, including the disconnected period.

Status codes

Errors come back as JSON with a detail field that says what went wrong.

CodeMeaningWhat to do
200Success. An empty list is a real answer (nothing matched), not a reason to retry.Use it.
202Your advertiser is recognised but Quorum has not finished mapping it (PENDING).Back off and retry later.
400Bad input the route checks itself, such as a reversed or too-wide date window on the partner feed, or a duplicate id in one intake request.Fix the request.
401Missing header, unknown token, or expired token.Check the token; ask Quorum for a new one if it expired.
403The token is not allowed this request: a partner token on a device- or household-level route, an advertiser outside your agency or outside the token's advertiser list, a read-only token on POST /v1/intake/*, or an intake body whose agency does not match the token.Do not retry. Read detail.
404Not found for your agency: an unknown advertiser id, a segment_md5 not configured for the advertiser, a missing ZIP, or no published report.Permanent for that input. For an unknown advertiser, POST it to /v1/intake/advertisers first.
410Your agency disconnected this advertiser.Do not retry. Reconnect it to restore access; nothing was deleted.
422Invalid parameter or body (FastAPI validation), or an advertiser Quorum has marked IGNORED/DEACTIVATED.Fix the request. An ignored advertiser is permanent.
429Per-IP rate limit, only on the library routes that state one (audience-intel: 3 per minute; cross-shop and cross-shop-dma: 10 per minute).Slow down.
500Unexpected server error.Contact api@quorum.live.
503Quorum cannot verify tokens or advertiser ownership right now, or a report payload is not servable yet.Retry shortly.

What changed (2026-11-10, API 2.1.0)

  • Partner scope. Partner tokens read only their own agency's advertisers, and the device- and household-level routes are refused with 403. See Access and scope.
  • Token expiry and read-only tokens. An expired token gets 401; a read-only token gets 403 on POST /v1/intake/*.
  • Published report numbers. New GET /v1/advertisers/{advertiser_id}/report and /reports.
  • Store status read-back. New GET /v1/intake/locations.
  • Disconnect / reconnect. New POST /v1/intake/advertisers/{client_advertiser_id}/disconnect, /reconnect and GET .../connection; reads for a disconnected advertiser return 410.
  • Quorum ids on /locations and /visits. A partner token may now pass a Quorum advertiser id its agency owns, not only a DSP id. Useful when your DSP ids are not crosswalked.
  • Intake is add-only. Re-posting a store returns the same location_id, with created and status per row. /locations now echoes your client_location_name and location_id.
  • Stores move to pending_draw once their advertiser is mapped and they have a usable address.
  • One DSP id, several Quorum accounts. /locations and /visits serve the union.
  • /visits date window. Dates are UTC days. New opt-in end_exclusive=true for a [date_from, date_to) window; without it date_to is inclusive.
  • /locations fields. maid_count_60d is deprecated and always null; use the new device_days_60d. lat, lon, address, city and state are filled, and a stored text 'null' comes back as a JSON null.
  • /v1/health works as an alias of /health.
  • Removed: the library's free-text question route (ask). Ask open-ended questions through the Quorum MCP.
GET/v1/advertisersBearer

Every advertiser configured for your agency, with names. The advertiser_id it returns is a Quorum advertiser id; use it on the roll-ups, /v1/performance/*, /v1/segments/* and /v1/partner-feed/*. No parameters.

FieldTypeDescription
agency_idintegerYour agency id.
agency_namestring|nullAgency name.
advertiser_idintegerQuorum advertiser id.
advertiser_namestring|nullAdvertiser name.
curl "https://api.quorum.live/v1/advertisers" \
  -H "Authorization: Bearer <token>"
GET/v1/advertisers/{advertiser_id}/reportBearer

The numbers on your published Quorum report, as data. They come from the same build as the report page, so every figure reconciles with the page. Schema quorum.retail_report.v1.

advertiser_id is your own DSP advertiser id, or a Quorum advertiser id your agency owns. You only ever see reports registered to your agency. A DSP id that is pending, disconnected or ignored answers 202, 410 or 422 as on /locations.

Query parameters

ParameterTypeDescription
report_idstringPick one report when the advertiser has several (see /reports).
periodstringReport period, YYYYMM.
versioninteger ≥ 1A specific published version. Default: the latest.

What is in the payload

  • headline, daily, weekly, campaigns, publishers, creatives, stores, zips, time_to_visit, plus definitions, windows (with mature and matures_on), attribution_days, kmin and coverage_multiplier. Sections the page does not show are [].
  • Visit: one household visiting any of the locations on one calendar day, deduplicated, attributed within attribution_days of an ad exposure. visit_rate = visits / impressions.
  • *_det fields are measured (deterministic) households. *_est = *_det × coverage_multiplier.
  • The row-level tables carry the measured visits_det. The page shows those cells grossed up, so page value = round(visits_det × coverage_multiplier). Each row's visit_rate is the page's own rate.
  • Cells under the reporting floor (k ≥ 3) are null with suppressed: true (or a withheld_reason). The page withholds them too.
  • Rates are fractions (0.00096, not 0.096%). Dates are the report's local dates, YYYY-MM-DD.
  • publication is added by the API: report_id, version, period, quorum_advertiser_ids, published_at, builder_sha, mature, matures_on, and other_report_ids when the advertiser has more than one report.
  • While mature is false, visits can still attribute and the figures can only go up.

404 when there is no published report for the advertiser (and filters). 503 if the stored payload is not in a servable format.

curl "https://api.quorum.live/v1/advertisers/12345/report" \
  -H "Authorization: Bearer <token>"
GET/v1/advertisers/{advertiser_id}/reportsBearer

Every published version of every report for the advertiser, newest first, without the numbers. Optional period (YYYYMM). Response: {"advertiser_id": ..., "reports": [ {report_id, version, period, quorum_advertiser_ids, published_at, builder_sha, mature, matures_on} ]}.

curl "https://api.quorum.live/v1/advertisers/12345/reports?period=202610" \
  -H "Authorization: Bearer <token>"
GET/v1/advertisers/{advertiser_id}/configBearer

The advertiser's capability flags. advertiser_id is a Quorum advertiser id; partner tokens get 403 for an advertiser outside their agency. The same applies to every roll-up below, and each of them takes date_from / date_to (YYYY-MM-DD, default the last 30 days).

FieldTypeDescription
advertiser_idintegerQuorum advertiser id.
display_namestring|nullAdvertiser name.
has_storebooleanStore-visit reporting is configured (report types 1, 4 or 6).
has_webbooleanWeb-visit reporting is configured (report types 2 or 5).
report_type_idsstring|nullComma-separated report type ids.
attribution_windowintegerDays from exposure to visit that count. Default 28.
config_statusstringACTIVE, IMPRESSIONS_ONLY or NOT_CONFIGURED.
curl "https://api.quorum.live/v1/advertisers/1234/config" -H "Authorization: Bearer <token>"
GET/v1/advertisers/{advertiser_id}/tab-availabilityBearer

Which report tabs have data in a date range (date_from, date_to, default the last 30 days). Use it before drawing a report UI.

FieldDescription
impressionsThe publisher table has rows in range.
store_visits / web_visitsFrom the config flags, not a data check.
publisher / geo / creative / traffic / householdThe matching performance table has rows in range.
lifthas_store and impressions are both true.
advertiser_id, config_statusAs on /config.
curl "https://api.quorum.live/v1/advertisers/1234/tab-availability?date_from=2026-10-01&date_to=2026-10-31" \
  -H "Authorization: Bearer <token>"
GET/v1/advertisers/{advertiser_id}/summaryBearer

Headline metrics over date_from..date_to. Impressions and reach come from the publisher performance table; visits from the household attribution tables, scaled by a coverage multiplier.

The roll-ups (/summary, /timeseries, /campaigns, /lineitems, /lift) use a different method from your published report and will not match it. To reproduce the numbers on your report page, use /report.

FieldDescription
impressions / device_reach / household_reachFrom the publisher table.
store_panel_reach / web_panel_reachMeasured households that saw an ad and then visited.
store_visits / web_visitspanel reach × multiplier.
store_visit_rate / web_visit_rateVisits / impressions.
multiplier / multiplier_grain / multiplier_significantCoverage multiplier and the grain it came from (line item, campaign, advertiser, or a global default).
hh_resolution_rateShare of impressions that resolved to a household.
platform_count / campaign_count / lineitem_countCounts in range.
has_store / has_webConfig flags.
start_date / end_date / data_start / data_endRequested window and the dates that actually had data.
curl "https://api.quorum.live/v1/advertisers/1234/summary?date_from=2026-10-01&date_to=2026-10-31" \
  -H "Authorization: Bearer <token>"
GET/v1/advertisers/{advertiser_id}/timeseriesBearer

One row per day: log_date, impressions, device_reach, household_reach, store_visits, store_visit_rate, web_visits, web_visit_rate. Visits are multiplier-scaled. Params: date_from, date_to.

curl "https://api.quorum.live/v1/advertisers/1234/timeseries?date_from=2026-10-01&date_to=2026-10-31" \
  -H "Authorization: Bearer <token>"
GET/v1/advertisers/{advertiser_id}/campaignsBearer

One row per insertion order: io_id, io_name, impressions, device_reach, household_reach, store_panel_reach, store_visits, store_visit_rate, web_panel_reach, web_visits, web_visit_rate, multiplier, first_date, last_date. Params: date_from, date_to.

curl "https://api.quorum.live/v1/advertisers/1234/campaigns?date_from=2026-10-01&date_to=2026-10-31" \
  -H "Authorization: Bearer <token>"
GET/v1/advertisers/{advertiser_id}/lineitemsBearer

One row per line item, with the same fields as /campaigns plus li_id and li_name. Params: date_from, date_to, and optional io_id to limit to one campaign.

curl "https://api.quorum.live/v1/advertisers/1234/lineitems?date_from=2026-10-01&io_id=IO-456" \
  -H "Authorization: Bearer <token>"
GET/v1/advertisers/{advertiser_id}/liftBearer

Visit rate of exposed households against non-exposed visitors. Params: date_from, date_to.

FieldDescription
exposed_hhHouseholds with at least one impression.
exposed_store_visitors / exposed_store_visit_rateExposed households that visited, and their share.
exposed_web_visitors / exposed_web_visit_rateThe same for web visits.
control_store_visitors / control_store_visit_rateBaseline from non-exposed visitors.
lift_pct(exposed rate − control rate) / control rate × 100.
confidenceSimple 0–99 score from the exposed sample size.
has_store / has_webConfig flags.
curl "https://api.quorum.live/v1/advertisers/1234/lift?date_from=2026-10-01&date_to=2026-10-31" \
  -H "Authorization: Bearer <token>"
GET/v1/advertisers/{advertiser_id}/store-visits/brandsBearer

Store visits by brand and category. Response: advertiser_id, total_visits, unique_households, and brands[] with brand, category, visits (measured, not scaled), unique_households, avg_days_to_visit. Params: date_from, date_to.

curl "https://api.quorum.live/v1/advertisers/1234/store-visits/brands?date_from=2026-10-01&date_to=2026-10-31" \
  -H "Authorization: Bearer <token>"

Locations and visits

These routes give you the stores Quorum tracks for an advertiser and the visits it observes there.

RouteWhat it answers
GET .../locationsWhich locations are tracked, with metadata, coordinates and 60-day activity. Takes your DSP id or an owned Quorum id.
GET .../locations/{segment_md5}/visitsEvery device observation at one location, paginated for bulk export. Takes your DSP id or an owned Quorum id.
GET .../locations/{segment_md5}/visitor-geographyWhere a location's visitors live, by census block. Takes the Quorum id.
GET .../locations/{segment_md5}/nearby-competitorsNearby locations in a category. Takes the Quorum id.

Typical workflow

  1. POST your advertiser to /v1/intake/advertisers and your stores to /v1/intake/locations.

  2. Poll GET /v1/intake/locations until stores are active; each active store lists its segment_md5s.

  3. Call /locations to see every tracked location (yours carry your client_location_name).

  4. Pull /visits per segment_md5 for a date window.

Polling

/locations and /visits resolve your id first (DSP id, then a Quorum id your agency owns). 200 (even with an empty list) means mapped; 202 means mapped soon, retry; 404 unknown for your agency; 410 disconnected; 422 ignored. Only 202 is a retry signal.

Household ids

The household_id on visit records is, for partner tokens, a stable 32-character HMAC of the household: the same household always gets the same value, so you can deduplicate and match on it, but it cannot be turned back into a location. The finest geography returned is the census block.

GET/v1/advertisers/{advertiser_id}/locationsBearer

Every location configured for the advertiser, both POIs (stores, venues) and Attractions (zones), whether or not visits have been seen. advertiser_id is your own DSP advertiser id, or a Quorum advertiser id your agency owns. Locations with no visits in 60 days are included unless you set min_households.

Query parameters

ParameterTypeDefaultDescription
location_typestring—POI or ATTRACTION. Omit for both.
brandstring—Case-insensitive exact brand match.
min_householdsinteger0Minimum 60-day household count. 1 drops locations with no traffic.
limitinteger5000Max rows, up to 50,000.

Response fields

FieldTypeDescription
segment_md5stringQuorum's id for the location. Use it on /visits.
location_typestringPOI or ATTRACTION.
name / brand / categorystring|nullQuorum's name for the location, its brand and category.
client_location_namestring|nullYour own label for the store, exactly as you sent it to POST /v1/intake/locations. Null for locations you did not submit, or not yet drawn. One label can sit on several segment_md5s.
location_idinteger|nullThe intake location_id for that label (informational).
lat / lonfloat|nullLocation coordinates.
address / city / state / zip_codestring|nullAddress.
dma_name / regionstring|nullDMA and region.
visits_30dinteger|nullMeasured visits in the last 30 days (not scaled).
household_count_60dintegerDistinct households seen in the last 60 days. 0 if none.
device_days_60dintegerDevice-days in the last 60 days (a device seen on two days counts twice).
maid_count_60dnullDeprecated, always null. Use device_days_60d.

Text fields never carry the string "null": a missing value is a JSON null.

curl "https://api.quorum.live/v1/advertisers/12345/locations?location_type=POI&min_households=1" \
  -H "Authorization: Bearer <token>"
GET/v1/advertisers/{advertiser_id}/locations/{segment_md5}/visitsBearer

Raw visit records for one location: one row per device observation, with household resolution and IP enrichment. advertiser_id is your own DSP advertiser id, or a Quorum advertiser id your agency owns. 404 if the segment_md5 is not configured for the advertiser.

Query parameters

ParameterTypeDefaultDescription
date_fromdate60 days before date_toFirst UTC day of the window, always inclusive (YYYY-MM-DD).
date_todatetoday (UTC)Last UTC day. Inclusive unless end_exclusive=true.
end_exclusivebooleanfalsefalse: window is [date_from, date_to]. true: window is [date_from, date_to), so if each pull starts where the last one ended, no day is returned twice.
limitinteger100,000Rows per page, up to 1,000,000.
offsetinteger0Row offset. Add limit to get the next page.

Response fields

FieldTypeDescription
segment_md5stringThe location.
segment_namestring|nullQuorum's name for the location.
maidstring|nullMobile advertising id seen at the location.
household_idstring|nullStable 32-character opaque key for partner tokens (see Household ids). Null if the device did not resolve.
visit_datedateUTC calendar day of the visit.
visit_timestampdatetime|nullTime of the observation, in UTC (no offset in the string).
capture_ipstring|nullIP seen at capture (the device's, not the household's).
hh_primary_ipstring|nullBest household IPv4: the most-observed IP for this device in the last 30 days.
ip_last_seendate|nullLast time hh_primary_ip was seen.
ip_observation_countinteger|nullHow often that IP was seen for this device. Higher is more reliable.
capture_census_blockstring|nullCensus block of the location.
hh_census_blockstring|nullHome census block of the household.
capture_lat / capture_lonfloat|nullCoordinates of the observation, when available.

Order and paging: rows are sorted by visit_date descending, then maid ascending. The most recent 1–3 days are still filling in.

curl "https://api.quorum.live/v1/advertisers/12345/locations/abc123def456.../visits?date_from=2026-10-01&date_to=2026-11-01&end_exclusive=true&limit=100000&offset=0" \
  -H "Authorization: Bearer <token>"
GET/v1/advertisers/{advertiser_id}/locations/{segment_md5}/visitor-geographyBearer

Where a location's visitors live, from a pre-built table over a rolling 90-day window. No date parameters. advertiser_id here is the Quorum advertiser id (from GET /v1/advertisers), and the location must be configured for it in your agency, otherwise 404.

FieldTypeDescription
segment_md5stringThe location.
window_start / window_enddate|nullThe window actually used.
total_devices_at_locationintegerDistinct devices seen at the location in the window.
census_blocks[]arrayHome census blocks, highest visitor index first. Each: census_block_id, zip_code, device_count, cblock_population, cblock_households, cblock_visitor_index (1.5 = 50% more visitors than its population share predicts), zip_visitor_index.
curl "https://api.quorum.live/v1/advertisers/1234/locations/abc123def456.../visitor-geography" \
  -H "Authorization: Bearer <token>"
GET/v1/advertisers/{advertiser_id}/locations/{segment_md5}/nearby-competitorsBearer

Library locations in a category near an anchor location, nearest first. The advertiser's own locations are left out. advertiser_id is the Quorum advertiser id; the anchor must be configured for it in your agency and have coordinates, otherwise 404.

ParameterTypeDefaultDescription
category_patternstring (required)—Category to match. A bare word matches any category containing it; * or % are wildcards (restaur*, *QSR*).
radius_milesfloatby category0.1–100. If omitted: auto/dealer 25, hotel 15, home improvement/furniture 10, gym/fitness 8, restaurant 5, grocery/pharmacy/cafe 3, gas 2, anything else 5.
limitinteger200Up to 2,000.

Fields: segment_md5, name, brand, category, distance_miles, lat, lon, address, city, state, dma_name, visits_30d, household_count_60d.

curl "https://api.quorum.live/v1/advertisers/1234/locations/abc123def456.../nearby-competitors?category_pattern=restaurant&radius_miles=3" \
  -H "Authorization: Bearer <token>"
POST/v1/intake/advertisersBearer, write

Add or update your advertisers, keyed on your own client_advertiser_id. A new id opens a PENDING mapping that Quorum completes; nothing is ever deleted. The same id twice in one request is a 400.

FieldTypeRequiredDescription
qurum_ag_idintegerYesYour agency id; must equal your token's agency (403 otherwise). Also accepted as agency_id.
records[]array (1–10,000)YesEach: client_advertiser_id (integer, required), client_advertiser_name (required), website, status.
records[].statusstringNoACTIVE or RETIRED (= disconnected). Omit to leave the stored status unchanged; a new advertiser starts ACTIVE.

Response: accepted, new_client_advertiser_ids.

curl -X POST https://api.quorum.live/v1/intake/advertisers \
  -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{"qurum_ag_id": 1234, "records": [{"client_advertiser_id": 12345, "client_advertiser_name": "Example Brand", "website": "example.com"}]}'
GET/v1/intake/advertisersBearer

Your advertisers and their Quorum mapping. Optional client_advertiser_id filter. Fields: client_advertiser_id, client_advertiser_name, website, status, quorum_advertiser_id (null until mapped), mapping_status (PENDING | MAPPED | IGNORED), mapped_at, location_count, status_updated_at.

curl "https://api.quorum.live/v1/intake/advertisers?client_advertiser_id=12345" \
  -H "Authorization: Bearer <token>"
POST/v1/intake/advertisers/{client_advertiser_id}/disconnectBearer, write

From the next request on, every route that reads this advertiser returns 410, whether you call it with your own advertiser id or the Quorum advertiser id: /locations, /visits, /report, /performance/*, /summary and the rest. Nothing is deleted, capture keeps running, and the Quorum advertiser, your stores and their locations are kept. Optional body {"reason": "..."} (kept in Quorum's audit log). Idempotent: repeating it returns 200 with changed: false.

POST/v1/intake/advertisers/{client_advertiser_id}/reconnectBearer, write

Serves the advertiser again, with its full history, including visits captured while it was disconnected. Same optional body. Idempotent.

GET/v1/intake/advertisers/{client_advertiser_id}/connectionBearer

The current state. All three return client_advertiser_id, agency_id, status (ACTIVE | RETIRED), connected, status_updated_at, and on POST changed. 404 if the id is unknown to your agency; 403 on the POSTs for a read-only token.

curl -X POST https://api.quorum.live/v1/intake/advertisers/12345/disconnect \
  -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{"reason": "campaign paused"}'
curl https://api.quorum.live/v1/intake/advertisers/12345/connection \
  -H "Authorization: Bearer <token>"
POST/v1/intake/locationsBearer, write

Send your stores. Add-only: send the full list or just the new stores. Stores already on file are kept, new ones are added, and a store you leave out is never deleted.

  • Your location_name is stored exactly as sent. It is the label Quorum gives back everywhere (GET /v1/intake/locations, and client_location_name on /locations).
  • A store row is identified by client_advertiser_id + location_name + address + city + state + zip_code. Re-posting the same row returns the same location_id with created: false.
  • A store moves from submitted to pending_draw once its advertiser is mapped and it has a street address plus a ZIP or city and state. Quorum then draws its footprint and it becomes active.
FieldTypeRequiredDescription
qurum_ag_idintegerYesYour agency id (or agency_id); must equal your token's agency.
records[]array (1–10,000)YesOne per store.
records[].client_advertiser_idintegerYesYour advertiser id.
records[].location_namestringYesYour store label.
records[].address / city / state / zip_codestringNoStreet address. state is two letters; a 1–4 digit ZIP is zero-padded to 5.
records[].store_urlstringNoStore page URL.

Response

FieldDescription
acceptedRecords in the request.
inserted / already_presentNew rows, and rows that were already on file.
new_client_advertiser_idsAdvertisers seen for the first time.
locations[]One per record, in request order: client_advertiser_id, location_name, location_id, created, status (submitted | pending_draw | active).
curl -X POST https://api.quorum.live/v1/intake/locations \
  -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{"qurum_ag_id": 1234, "records": [{"client_advertiser_id": 12345, "location_name": "Example Brand #12", "address": "123 Main St", "city": "Dallas", "state": "TX", "zip_code": "75201"}]}'
GET/v1/intake/locationsBearer

The stores you have posted, one row per store, with their status.

StatusMeaning
submittedReceived. Waiting for the advertiser to be mapped, or for a usable address.
pendingQueued for Quorum to draw the footprint (the POST echo calls this pending_draw). No visits yet.
activeFootprint drawn; segment_md5s lists the location ids. A new footprint back-fills about 85 days of visits on its first nightly run.

Query parameters

ParameterTypeDescription
client_advertiser_idintegerOnly this advertiser (your id).
statusstringsubmitted, pending or active.
location_namestringExact, case-sensitive label match.
after_location_idintegerPaging cursor: the previous page's next_after_location_id. Omit for the first page.
limitintegerPage size, 1–5000. Default 1000.

Response: agency_id, status_counts (submitted, pending, active, for the same filters ignoring status and paging), returned, next_after_location_id (null on the last page), and locations[] with location_id, client_advertiser_id, location_name, address, city, state, zip_code, store_url, status, segment_md5s, submitted_at, last_submitted_at, status_updated_at (UTC). No geometry is returned.

curl "https://api.quorum.live/v1/intake/locations?client_advertiser_id=12345&status=active" \
  -H "Authorization: Bearer <token>"
GET/v1/partner-feed/dailyBearer

Daily exposure and visit counts for your agency, one row per day × advertiser (or campaign) × metric set. JSON by default, CSV with format=csv or Accept: text/csv.

ParameterTypeDescription
date_from / date_todate (required)Inclusive window, at most 31 days. A wider or reversed window is a 400.
grainstringadvertiser (default) or campaign.
advertiser_idintegerQuorum advertiser id. Omit for all of your agency's advertisers.
metric_setstringComma-separated subset of EXPOSURE, VISITS_STORE, VISITS_WEB. Default: all three.
formatstringjson or csv.

Row fields: activity_date, agency_id, advertiser_id, advertiser_name, campaign_id, campaign_name, metric_set, impressions, hh_resolved_impressions, det_count, est_count, suppressed, multiplier, multiplier_grain, multiplier_significant, config_status, unique_households_last_touch, source_table, generated_at.

  • det_count is measured households, never scaled. est_count = det_count × multiplier, and is null when the multiplier is not statistically significant.
  • A row whose det_count is above 0 but under the privacy floor (currently 50 households) has det_count and est_count null and suppressed: true. A 0 always means a verified zero.
curl "https://api.quorum.live/v1/partner-feed/daily?date_from=2026-10-01&date_to=2026-10-31&grain=campaign&format=csv" \
  -H "Authorization: Bearer <token>"
GET/v1/partner-feed/reconcileBearer

Window totals from the feed for one advertiser, beside its hosted report, so you can tell whether they should match. Params: advertiser_id (Quorum id), date_from, date_to (all required; 31 days max).

Response: advertiser_id, window, feed_total (impressions, det_household_reach, est_household_reach), hosted_report (report_id, report_type, refreshed_at, note, or null), match, match_reason.

curl "https://api.quorum.live/v1/partner-feed/reconcile?advertiser_id=1234&date_from=2026-10-01&date_to=2026-10-31" \
  -H "Authorization: Bearer <token>"

Performance tables

Daily performance rows for one Quorum advertiser, filtered to your agency (an advertiser outside it returns an empty list). Shared parameters: advertiser_id (required), date_from (default 30 days ago), date_to (default today), io_id, li_id, limit.

Fields on every row: log_date, io_id, io_name, li_id, li_name, advertiser_id, platform_name, impressions, device_reach, household_reach, visitors, visit_rate, panel_reach, multiplier, web_visitors, total_page_views, web_visit_rate.

RouteExtra fieldsNotes
GET /v1/performance/publisherspublisher, supply_vendor, publisher_code, site_id, publisher_id, supply_type_idlimit default 5000, max 50,000.
GET /v1/performance/geodma, zip, census_block_idExtra filter dma (code or name). limit default 5000, max 50,000.
GET /v1/performance/trafficurl, url_typelimit default 5000, max 50,000.
GET /v1/performance/creativecreative_id, creative_name, creative_sizelimit default 5000, max 50,000.
GET /v1/performance/householdshousehold_id, census_block_idInternal tokens only
curl "https://api.quorum.live/v1/performance/publishers?advertiser_id=1234&date_from=2026-10-01&io_id=IO-456" \
  -H "Authorization: Bearer <token>"

Segments

Location segments configured for a Quorum advertiser in your agency.

GET/v1/segments/catalogBearer

Params: advertiser_id (required), limit (default 1000, max 10,000). Fields: agency_id, quorum_advertiser_id, location_type, segment_md5, name, brand, category, entity_type, dma_name.

curl "https://api.quorum.live/v1/segments/catalog?advertiser_id=1234" -H "Authorization: Bearer <token>"
GET/v1/segments/statsBearer

30-day counts per segment, ending 5 days ago for data lag. Params: advertiser_id (required), limit (default 1000). Fields: segment_md5, segment_visitors_30d (distinct households), active_days, total_device_visits.

curl "https://api.quorum.live/v1/segments/stats?advertiser_id=1234" -H "Authorization: Bearer <token>"
GET/v1/advertisers/{advertiser_id}/segmentsBearer

POI segments with household visit stats over date_from..date_to (default the last 90 days). Filters: brand, category, min_households, limit (default 5000, max 50,000). Fields: segment_md5, name, brand, category, entity_type, dma_name, lat, lon, household_count, device_count, first_visit, last_visit.

curl "https://api.quorum.live/v1/advertisers/1234/segments?min_households=1" -H "Authorization: Bearer <token>"

Reference data

GET/v1/zips/{zip_code}Bearer

zip_code, city, state, state_name, county, dma_code, dma_name, population, population_density, latitude, longitude. 404 if unknown.

curl https://api.quorum.live/v1/zips/10001 -H "Authorization: Bearer <token>"
GET/v1/census-blocks/{census_block_id}Bearer

For a 15-digit FIPS block: census_block_id, zip_code, city, state, state_name, state_fips, county_fips, tract_fips, dma_code, dma_name, zip_population. 404 if unknown.

GET/v1/locations/{segment_md5}Bearer

Library metadata for any segment_md5: segment_name, brand, category, dma_name, zip_code, region, visits_30d, source_agency_id, source_advertiser_id. 404 if not in the library.

Location library

Aggregate data on Quorum's reference library of tracked US locations (brands, categories, DMAs). Open-ended questions go to the Quorum MCP at https://mcp.quorum.live/mcp; the library's free-text question route has been removed.

RouteAuthWhat it does
GET /v1/library/categoriesPublicTop categories by location count and 30-day visits. Param limit (default 25, max 100).
POST /v1/library/audience-intelPublicAudience bundle for a brand: footprint, top DMAs and locations, cross-shopping, cohorts, media affinity. Body {"brand": "..."}. 3 requests per minute per IP.
GET /v1/library/searchBearerFind locations. At least one of category, brand, dma, zip_code; plus sort_by (visits_30d or name) and limit (max 100).
GET /v1/library/statsBearerLibrary-wide totals.
GET /v1/library/brandsBearerBrand search for pickers: q, category, limit.
GET /v1/library/cross-shopBearerWhere a brand's visitors also go (sampled). brand (required), dma, limit (max 30), sample_size. 10 per minute per IP.
GET /v1/library/cross-shop-dmaBearerCross-shopping for one brand in one DMA: brand, dma, limit (max 30). 10 per minute per IP.
POST /v1/library/audience-reportBearerA written audience report for a brand. Body {"brand": "..."}.
POST /v1/library/personasBearerPersona mix of the households that visit a brand. Body {"brand": "..."}.
POST /v1/library/activateBearerAsk Quorum's team to activate a brand audience on your DSP. Body needs brand and email; optional dsp, seat_id, devices, markets, category.
curl "https://api.quorum.live/v1/library/categories?limit=10"

Audience intelligence reports

GET/v1/reportsBearer

The published brand audience-intelligence reports: {"reports": [...], "count": N}.

GET/v1/reports/{slug}Bearer

One report as JSON: master, headline_stats, dma_distribution, category_affinity, cross_shopping, competitive_set, narratives. 404 for an unknown slug.

These are brand-level reports, not your campaign reports; for those see /report.

Internal tokens only

Partner tokens get 403 on every route on this page, with a pointer to the Quorum MCP (https://mcp.quorum.live/mcp). They are listed so the API surface is complete.

RouteWhat it returns (internal tokens)
POST /v1/resolveBody {"input_type": "ipv4" | "maid" | "latlong", "value": "...", "max_distance_m": 30}. Returns household_id, centroid_distance_m, maid_count, maids[] (maid, primary_ip, census_block_id, state).
GET /v1/household/{household_id}/geocensus_block_id, zip_code, dma_code, dma_name, state, maid_count.
GET /v1/household/{household_id}/visitsSegment visits for one advertiser (advertiser_id required; date_from, date_to as a half-open UTC window; limit).
GET /v1/household/{household_id}/footprintEvery library location the household visited (date_from, date_to, limit).
GET /v1/household/{household_id}/publishersPublisher exposure by day.
GET /v1/segments/{segment_md5}/visitsPer-device visits, one row per device per day.
GET /v1/segments/{segment_md5}/visitor-geographyVisitors' home ZIPs and DMAs.
GET /v1/locations/{segment_md5}/visitor-geographyLibrary visitor geography by census block and ZIP.
GET /v1/advertisers/{advertiser_id}/segments/{segment_md5}/householdsHouseholds that visited a segment.
GET /v1/performance/householdsOne attribution row per household.

Data freshness

DataFreshness
Visit records (/visits)Loaded nightly. The most recent 1–3 days are still filling in.
New store footprintsBack-fill about 85 days of visits on their first nightly run.
Published reports (/report)As of each published version; mature turns true once the attribution window has closed.
Rolling 30-day segment countsWindow ends 5 days ago.
Geographic reference dataStatic, refreshed periodically.