UNDERCART API
Query enriched Shopify store data — revenue estimates, technology stacks, ad presence, and more — over HTTPS with a single API-key header. Every response is JSON.
Pointing an AI agent at this? Skip the HTTP — add Undercart as an MCP connector and ask in natural language.MCP docs →Overview
The Undercart REST API is served under the base URL below. All endpoints require authentication and return a consistent JSON envelope. Endpoints that expose store data count against your plan's monthly lookup quota.
Base URL: https://undercart.co/api/v1
Authentication
Authenticate every request with an API key in the X-API-Key header. Create and manage keys from the dashboard (Settings → API keys); the full key is shown once at creation and stored only as a hash. Keys look like sk_live_…. A logged-in browser session cookie also authenticates requests to the same endpoints.
curl https://undercart.co/api/v1/me \ -H "X-API-Key: sk_live_9f2a…"
Response format
Every endpoint returns the same envelope. On success, data holds the payload; on failure, error holds a human-readable message. List endpoints add a meta object with pagination details.
{
"success": true,
"data": { /* endpoint payload */ },
"meta": { // list endpoints only
"limit": 50,
"total": 681240,
"totalIsApproximate": true, // true for the unfiltered catalog count
"nextCursor": "eyJ2Ijo…", // pass back as ?cursor= for the next page
"prevCursor": null
}
}List endpoints use keyset (cursor) pagination: read meta.nextCursor and pass it back as ?cursor= with dir=next (or prev). A null cursor means there are no more pages in that direction.
Errors & rate limits
Errors use standard HTTP status codes with success: false and an error message.
| Parameter | Type | Description |
|---|---|---|
| 400 | Bad Request | Invalid or missing query/body parameters. |
| 401 | Unauthorized | Missing or invalid API key / session. |
| 403 | Forbidden | Authenticated but lacking permission (admin-only route). |
| 404 | Not Found | No matching store. |
| 429 | Too Many Requests | Monthly store-lookup quota reached; the message includes the plan cap and reset date. |
| 500 | Server Error | Unexpected error. |
Store-data endpoints meter one store access per requestagainst your plan's monthly cap. When you exceed it the API returns 429 until the quota resets at the start of the next month.
List / search stores
/api/v1/storesSearch and filter the store catalog. Returns a page of store summaries. By default the full catalog is in scope (including dormant, not-yet-enriched discovery rows); pass enriched=true to restrict to fully enriched stores.
| Parameter | Type | Description |
|---|---|---|
| q | string | Case-insensitive match on domain or store name. |
| country | string | ISO country filter. |
| category | string | Store category filter. |
| revenueMin / revenueMax | number | Estimated annual revenue bounds (USD). |
| shopifyPlan | string | Filter by detected Shopify plan. |
| techStack | string | Comma-separated app/tech names; matches stores using all of them. |
| enriched | boolean | true/false — restrict to enriched stores only. |
| trackingEmail | boolean | true/false — only stores whose email marketing is being tracked (newsletter subscribed and at least one email captured). |
| sort | enum | estimatedAnnualRevenue | productCount | lastCrawledAt | firstSeenAt | name | domain (default: estimatedAnnualRevenue). |
| order | enum | asc | desc (default: desc). |
| cursor / dir | string | Keyset pagination cursor and direction (next | prev). |
| limit | number | Page size, 1–100 (default: 50). |
curl https://undercart.co/api/v1/stores \
-H "X-API-Key: sk_live_9f2a…" \
-G -d "revenueMin=1000000" \
-d "techStack=klaviyo" \
-d "sort=estimatedAnnualRevenue" \
-d "limit=2"
# → 200 OK
{
"success": true,
"data": [
{
"id": "clw…",
"domain": "maplestone.co",
"name": "Maple & Stone",
"country": "US",
"category": "Home & Living",
"estimatedAnnualRevenue": 11400000,
"estimatedMonthlyRevenue": 950000,
"revenueConfidence": 0.72,
"revenueTier": "high",
"productCount": 1284,
"lastCrawledAt": "2026-06-29T02:00:00.000Z",
"techCount": 37,
"topTech": ["Shopify Plus", "Klaviyo", "Gorgias", "Recharge"]
}
],
"meta": { "limit": 2, "total": 4120, "nextCursor": "eyJ2Ijo…" }
}Get store by domain
/api/v1/stores/by-domain?domain=<domain>Returns the full enriched profile for a single known store, keyed by domain — revenue, plan, catalog stats, active tech stack (grouped), ad presence, and social profiles. Responds 404 if the domain is not in the catalog.
curl "https://undercart.co/api/v1/stores/by-domain?domain=maplestone.co" \
-H "X-API-Key: sk_live_9f2a…"
# → 200 OK
{
"success": true,
"data": {
"id": "clw…",
"domain": "maplestone.co",
"name": "Maple & Stone",
"category": "Home & Living",
"country": "US",
"foundedYear": 2019,
"shopifyPlan": "Shopify Plus",
"estimatedAnnualRevenue": 11400000,
"revenueConfidence": 0.72,
"productCount": 1284,
"avgProductPrice": 68.5,
"currency": "USD",
"techStack": [
{ "techName": "Klaviyo", "techCategory": "Email & SMS" }
],
"adPresence": [
{ "platform": "meta", "isActive": true, "lastCheckedAt": "2026-06-29T…" }
],
"socialProfiles": [
{ "platform": "instagram", "profileUrl": "https://…", "followers": 84000 }
]
}
}Look up or enqueue a crawl
/api/v1/stores/lookupLook up a store by domain. If it is already known, the full profile is returned immediately (200). If it is not yet in the catalog, a discovery/enrichment job is enqueued and the endpoint responds 202 Accepted with a job id.
curl -X POST https://undercart.co/api/v1/stores/lookup \
-H "X-API-Key: sk_live_9f2a…" \
-H "Content-Type: application/json" \
-d '{ "domain": "newstore.com" }'
# → 202 Accepted (store was not yet known)
{
"success": true,
"data": { "jobId": "123", "domain": "newstore.com", "status": "queued" }
}Get store by ID
/api/v1/stores/{storeId}Returns the complete store record by its Undercart ID, including the full tech stack, recent revenue history, ad presence, social profiles, and any traffic data.
Get store tech stack
/api/v1/stores/{storeId}/techReturns the store's detected technologies grouped by category (email/SMS, support, subscriptions, analytics, etc.), each with version, first/last-seen timestamps, and whether it is currently active.
Get revenue history
/api/v1/stores/{storeId}/revenueReturns the store's historical revenue estimates, most recent first.
| Parameter | Type | Description |
|---|---|---|
| limit | number | Number of estimates to return, 1–100 (default: 30). |
| method | enum | Filter to one signal: composite | traffic | tech_stack | product | ad_spend | social. |
Bulk export
/api/v1/stores/exportStreams a filtered result set for offline use. Accepts the same filters as the list endpoint (search, country, category, revenueMin/Max, shopifyPlan, enriched, trackingEmail, sort, order). Choose the output with format=csv (default) or format=json, and pick columns with a comma-separated columns= list. Exports are capped at 50,000 rows.
curl "https://undercart.co/api/v1/stores/export?format=json&revenueMin=1000000" \ -H "X-API-Key: sk_live_9f2a…" -o stores.json
Identity
/api/v1/meReturns the authenticated caller's id and role. Useful for verifying a key works.
{ "success": true, "data": { "id": "usr_…", "role": "user" } }