Sales are not open yet. Prices are indicative. Request pilot access

API documentation

A simple REST API. Send a request, get JSON back.

Authentication

Every call needs your API key, except /v1/sample. Send it in the X-API-Key header, or in an Authorization: Bearer header. Your key is on your Account page.

Keys start with eldb_live_. Keep yours secret: never put it in the code of a web page.

curl https://api.ecommerce-leads-database.com/v1/me \
  -H "X-API-Key: eldb_live_…"

# Or, if you prefer:
curl https://api.ecommerce-leads-database.com/v1/me \
  -H "Authorization: Bearer eldb_live_…"

Endpoints

All URLs start with https://api.ecommerce-leads-database.com.

GET/v1/sample

Free sample (no key)

Returns a few stores: 10 by default, 20 at most. Use it to see the data shape before subscribing. Does not count against any quota.

curl "https://api.ecommerce-leads-database.com/v1/sample?limit=10"
GET/v1/me

Your plan and quota

Returns your plan, requests and stores used, your databases and the reset date. Free: uses no request and no store.

curl https://api.ecommerce-leads-database.com/v1/me \
  -H "X-API-Key: eldb_live_…"

{
  "plan": "api_pro",
  "planLabel": "Pro",
  "requests": { "limit": 25000, "used": … },
  "stores": { "limit": 10000, "used": … },
  "databases": { "slots": 3, "selected": ["shopify", "woocommerce", "prestashop"] },
  "exports": true,
  "resetAt": "…"
}
GET/v1/stores/count

Count stores

Returns how many stores match the filters, and how many have a Google profile. Costs 1 request and 0 stores.

Parameters: platform, tld, country, hasGmb, hasEmail, hasPhone, app, currency, lang, operatorCountry, activeOnly, genericEmailsOnly, includeUnverified, includeAliases, maxChromeRank, mailProvider, dmarcPolicy, trackingId, hasDecisionMaker, hasDecisionMakerEmail

curl "https://api.ecommerce-leads-database.com/v1/stores/count?platform=shopify" \
  -H "X-API-Key: eldb_live_…"

{ "count": 29331, "gmbCount": 6511 }

Real figures for verified Shopify stores.

GET/v1/stores/search

Search stores

Filters the database and returns one page of results (items) with a cursor (nextCursor).

Parameters: platform, tld, country, minProducts, hasGmb, hasEmail, hasPhone, app, currency, lang, operatorCountry, activeOnly, genericEmailsOnly, includeUnverified, includeAliases, maxChromeRank, mailProvider, dmarcPolicy, trackingId, hasDecisionMaker, hasDecisionMakerEmail, cursor, limit

  • limit: 1 to 100 results per page. 50 by default.
  • Order: highest traffic first. Stores with no known traffic come last.
  • By default, search and count return verified stores only: a real online shop, re-checked on its own site. Add includeUnverified=true to also include brochure sites and unreachable sites.
  • activeOnly is true by default: stores scored inactive are left out. Add activeOnly=false to include them.
  • Filters: hasEmail (email collected in the last 12 months), hasPhone, app (a detected app, e.g. Klaviyo), currency (EUR), lang (fr), operatorCountry (FR), genericEmailsOnly (with hasEmail: generic mailboxes only). A bad value returns 400 invalid_parameter and the request is not charged.
  • maxChromeRank (highest Chrome UX Report ranking bucket: 1000, 5000, 10000, 50000, 100000, 500000, 1000000, 5000000, 10000000 or 50000000). Any other value returns 400 invalid_parameter. The audience field contains Chrome UX Report (Google) data, under the CC BY-SA 4.0 license.
  • mailProvider (email provider worked out from the MX: google-workspace, microsoft-365, ovh, infomaniak, ionos, gandi, zoho, proton, o2switch, hostinger, other, none) and dmarcPolicy (none, quarantine, reject or missing). Any other value returns 400 invalid_parameter. The app filter also finds tools read from DNS (for example Klaviyo through SPF). App slugs that DNS can add: klaviyo, mailchimp, brevo, shopify email, mailjet, sendgrid, hubspot, salesforce marketing cloud, omnisend, mandrill, amazon ses, zendesk, mailgun, meta business, google site verification, pinterest, apple, atlassian, zoom, stripe, shopify.
  • Public tracking IDs: use trackingId to find the other stores that share one code. Pass a bare value such as GTM-ABC123, AW-123456789, G-ABCDEF1234, UA-1234567-1 or ca-pub-1234567890123456. Numeric IDs need their type first: meta:123456789012345, tiktok:…, pinterest:…, microsoftAds:…, linkedin:…, hotjar:…, criteo:…, snapchat:…, clarity:…, klaviyo:… (Klaviyo is case-sensitive). Any other value returns 400 invalid_parameter. Each result also holds trackingIds (null until the store has been read).
  • hasDecisionMaker=true|false (at least one known decision maker, or none) and hasDecisionMakerEmail=true (at least one decision maker whose email was found on the site or accepted by the mail server). Any other value returns 400 invalid_parameter. Each result also holds hasDecisionMaker.
  • Next page: send the nextCursor value back in the cursor parameter. On the last page, nextCursor is null.
  • Every store returned counts against your store budget. A page of 50 results uses 50 stores.
  • Each result is a store summary: domain, platform, country, theme, product count, apps, traffic, merchant name, Google rating and category, hasEmail, hasPhone, hasDecisionMaker, currency, lang, operatorCountry, isActiveShop. Each result also holds audience (null when the store is not in the Chrome UX Report ranking). Each result also holds dns (null until the store has been read). For the full record, call /v1/stores/:domain.
curl -G https://api.ecommerce-leads-database.com/v1/stores/search \
  -H "X-API-Key: eldb_live_…" \
  -d platform=shopify \
  -d minProducts=50 \
  -d hasGmb=true \
  -d limit=50

{
  "items": [
    { "domain": "…", "platform": "shopify", "productCount": …, "merchantName": "…", … }
  ],
  "nextCursor": "18450:01J…"
}
GET/v1/stores/:domain

Store record

Returns the full record of one store. Costs 1 request and 1 store. The catalog is not included: use /catalog. Every email is returned with its type; add genericEmailsOnly=true to receive generic mailboxes only. genericEmailsOnly=true also hides decision-maker emails.

FieldContent
domainThe store's domain name.
platformDetected ecommerce platform (shopify, woocommerce…).
tldDomain extension.
countryCountry (ISO code).
themeDetected theme.
productCountNumber of products detected.
verificationStatusOutcome of the last site check.
lastSuccessAtDate of the last successful check.
firstSeenAtDate the store entered the database.
detectedAppsApps and tools detected on the site.
trafficMonthly traffic and organic keyword estimates.
audienceChrome audience: { chromeRank, class, country, month, source }. chromeRank is the Chrome UX Report (Google) ranking bucket for that country: 1000, 5000, 10000, up to 50000000. null if the store is not ranked. This field is shared under the CC BY-SA 4.0 license: credit the source and keep the same license. class values: top_1k, top_5k, top_10k, top_50k, top_100k, top_500k, top_1m, top_5m, top_10m, top_50m. Chrome UX Report source: https://developer.chrome.com/docs/crux. License: https://creativecommons.org/licenses/by-sa/4.0/. Data adapted: ranks matched to stores and grouped by country by us.
dnsDNS: { mailProvider, dmarcPolicy, apps, checkedAt }. mailProvider is the email provider worked out from the MX (google-workspace, microsoft-365, ovh, infomaniak, ionos, gandi, zoho, proton, o2switch, hostinger, other, none). dmarcPolicy is none, quarantine, reject or missing (no record). apps lists the email sending and domain verification tools read from the SPF and TXT records. checkedAt is the date of the reading. null until the store has been read. No verification token is exposed.
trackingIdstrackingIds: public tracking and ad IDs grouped by type, for example { "gtm": ["GTM-ABC123"], "googleAds": ["AW-123456789"], "meta": ["123456789012345"] }. Types: ga4, ua, gtm, googleAds, floodlight, adsense, meta, tiktok, pinterest, microsoftAds, linkedin, snapchat, criteo, clarity, hotjar, klaviyo. {} when nothing was found, null until the store has been read. Only codes that any visitor can see in the page source.
platformVersionPlatform version (PrestaShop, Magento, Odoo) when the site shows it.
operatorCountry of the business behind the store ({ country, source }): from its VAT number, Shopify settings, legal-notice address, phone or TLD, in that order. Can differ from the TLD.
isActiveShopfalse when the store looks inactive (empty catalog, demo products, default currency), with the reasons in activeShopReasons. null when not scored yet.
legalLegal-notice page: pageUrl, companyIds (SIREN, VAT, BE enterprise number, CH UID, checked by their check digits), legalForm.
shopifyShopify stores: shopId, isPlus, shipsToCountries, shopPayInstallments, collectionsCount, themeStoreId.
catalogStatsCatalog figures when the platform exposes them: average price and weight, variants, images, vendors, product types, categories, brands, newest product date.
metadatamerchantName; contactEmail with contactEmailType (generic, personal or unknown) and emailDomainMatch; telephone (E.164) with phoneType (mobile, fixed_line…); address; socialLinks; sources (page and date each contact was collected); title, metaDescription, lang, currency, hreflangs, features, paymentMethods, shippingCarriers, salesChannels, copyrightYear, contactPageUrl, canonicalUrl. A contact collected more than 12 months ago, or an email whose owner opted out, is null with the reason in contactEmailWithheld, telephoneWithheld or addressWithheld.
emailsEvery email the store publishes: [{ email, type, sourceUrl }]. type is generic, personal or unknown; sourceUrl is the page the email was read on. At most 10, best first. An email read more than 12 months ago, or removed by its owner, is not returned. With genericEmailsOnly=true, only generic emails remain.
decisionMakersDecision makers: [{ firstName, lastName, role, source, sourceUrl, since, email, emailStatus }], primary decision maker first. role: owner (owner, sole trader), president, managing_director, manager, publication_director (person legally responsible for the site), partner, administrator (board member), other. source: rne (national business register, France), zefix (commercial register, Switzerland) or legal_page (the store's legal notice). sourceUrl: the company's public registry page, or the legal notice page. since: start date in the role when the registry gives it, else null. emailStatus: found_on_site (address published on the site), valid (accepted by the domain's mail server), catch_all (the server accepts every address: most likely format, not confirmed), pattern (worked out from the domain's address format, not checked), not_found (no address accepted by the server), null (no known address). email is null when its owner removed it, when it is more than 12 months old, or with genericEmailsOnly=true. A decision maker read more than 12 months ago is no longer returned. Never a birth date, nationality or home address.
companyCompany registry record: { registry, registryId, officialName, activityCode, headcountBand, createdOn, category, legalFormCode, active, hq, checkedAt }. registry is fr, be or ch; registryId is the SIREN, the BE enterprise number or the CH UID. officialName: registered name. activityCode: activity code (NAF or NACE). headcountBand: employee headcount band, for example "10-19". createdOn: creation date. category: company category (France). legalFormCode: the registry's legal form code. active: false when the company is closed. hq: postcode and town of the head office. checkedAt: date the registry was last read. A field the registry does not give is null. company is null when the store has no company number, when the registry does not know it, or when the reading is more than 12 months old.
gmbGoogle profile: placeId, category, additionalCategories, rating, ratingDistribution, isClaimed, totalPhotos, workTime, address, addressInfo, latitude, longitude, phone, website. null when there is none.
curl https://api.ecommerce-leads-database.com/v1/stores/probikeshop.fr \
  -H "X-API-Key: eldb_live_…"

{
  "store": {
    "domain": "probikeshop.fr",
    "platform": "shopify",
    "tld": "fr",
    "country": "FR",
    "theme": "…",
    "productCount": 5000,
    "verificationStatus": "…",
    "lastSuccessAt": "…",
    "detectedApps": ["…"],
    "trackingIds": { "gtm": ["GTM-ABC123"], "googleAds": ["AW-123456789"] },
    "operator": { "country": "FR", "source": "vat" },
    "isActiveShop": true,
    "legal": { "pageUrl": "…", "companyIds": { "siren": "…", "vat": "…" }, "legalForm": "SAS" },
    "metadata": {
      "merchantName": "…",
      "contactEmail": "…",
      "contactEmailType": "generic",
      "contactEmailWithheld": null,
      "telephone": "+33…",
      "phoneType": "fixed_line",
      "address": { … },
      "sources": {
        "contactEmail": { "url": "https://…/mentions-legales", "at": "2026-10-19T08:00:00.000Z" }
      },
      "socialLinks": { "instagram": "…" },
      "currency": "EUR",
      "lang": "fr"
    },
    "emails": [
      { "email": "contact@…", "type": "generic", "sourceUrl": "https://…/contact" }
    ],
    "decisionMakers": [
      {
        "firstName": "Marie", "lastName": "Durand", "role": "president",
        "source": "rne", "sourceUrl": "https://annuaire-entreprises.data.gouv.fr/entreprise/…",
        "since": "2019-03-01", "email": "marie.durand@…", "emailStatus": "valid"
      }
    ],
    "company": {
      "registry": "fr", "registryId": "…", "officialName": "…",
      "activityCode": "47.91B", "headcountBand": "10-19", "createdOn": "…",
      "category": "PME", "legalFormCode": "5710", "active": true,
      "hq": "…", "checkedAt": "…"
    },
    "gmb": {
      "placeId": "…", "category": "…", "rating": 4.5, "votes": 594,
      "address": "…", "phone": "…", "website": "…"
    }
  }
}
GET/v1/stores/:domain/catalogPro and Scale

Product catalog

Returns the store's products, up to 5,000. Available for part of the Shopify and WooCommerce stores. Otherwise you get a 404 no_catalog, which is not billed. Costs 10 requests.

curl https://api.ecommerce-leads-database.com/v1/stores/probikeshop.fr/catalog \
  -H "X-API-Key: eldb_live_…"

{
  "domain": "probikeshop.fr",
  "catalog": [
    {
      "id": "…", "title": "…", "handle": "…", "vendor": "…",
      "productType": "…", "tags": ["…"],
      "price": …, "compareAtPrice": …, "currency": "EUR",
      "available": true, "imageUrl": "…", "imageCount": …, "variantCount": …,
      "createdAt": "…", "updatedAt": "…"
    }
  ]
}
GET/v1/stores/:domain/trendPro and Scale

Traffic trend

Returns the monthly history of organic traffic and positions. 404 no_trend when there is no data, not billed. Costs 2 requests.

curl https://api.ecommerce-leads-database.com/v1/stores/probikeshop.fr/trend \
  -H "X-API-Key: eldb_live_…"

{
  "domain": "probikeshop.fr",
  "trend": [
    { "date": "2026-07-01", "organicTraffic": 197021, "organicPositions": 19377 }
  ],
  "savedAt": "2026-07-01T04:12:08.441Z"
}

Enrich with your keys

Find the email and phone of a store's decision makers with your own Apollo, FullEnrich, Kaspr or Dropcontact account. The call uses your credits at the provider. The results are yours: no other customer sees them.

Save your provider keys in your account, on the My provider keys page. They never go through the API: a stolen ELDB key can neither read nor change them.

Per request: 25 stores and 3 decision makers per store. Per customer: 3 running enrichments, and 500 people per provider per day (UTC). A result is served for 12 months, then erased at 13 months.

GET/v1/enrichment/providers

Your providers

Lists the providers with a saved key, with their status and the credits left when the provider reports them. Free.

curl https://api.ecommerce-leads-database.com/v1/enrichment/providers \
  -H "X-API-Key: eldb_live_…"

{
  "providers": [
    { "provider": "dropcontact", "status": "valid", "creditsRemaining": 250 }
  ]
}
POST/v1/enrichments

Start an enrichment

Send provider and domain (one store) or domains (25 at most). maxPeoplePerStore: 1 to 3, 2 by default. phones: true to ask for phone numbers too (more credits). force: true to run again a person enriched less than 30 days ago. Costs 1 request and 1 store per store with decision makers. Answers 200 with the results when the provider answers at once, otherwise 202: follow the job.

curl -X POST https://api.ecommerce-leads-database.com/v1/enrichments \
  -H "X-API-Key: eldb_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "provider": "dropcontact", "domains": ["probikeshop.fr"], "maxPeoplePerStore": 2 }'

HTTP/1.1 202 Accepted
{
  "job": {
    "id": "…", "provider": "dropcontact", "status": "queued", "phones": false,
    "storeCount": 1, "personCount": 2, "creditsUsed": null, "errorCode": null,
    "createdAt": "…", "updatedAt": "…"
  },
  "results": [
    { "domain": "probikeshop.fr", "status": "ok", "people": [ … ] }
  ]
}
GET/v1/enrichments/:jobId

Follow an enrichment

Returns the job status (queued, submitted, done, partial, failed, canceled) and the results by store. Free. Wait at least 20 seconds between two calls.

curl https://api.ecommerce-leads-database.com/v1/enrichments/<jobId> \
  -H "X-API-Key: eldb_live_…"
GET/v1/stores/:domain/enrichment

A store's results

Returns the platform, the merchant name, the decision makers, your providers and your results. Costs 1 request and 1 store. DELETE on the same URL erases your results for this store.

curl https://api.ecommerce-leads-database.com/v1/stores/probikeshop.fr/enrichment \
  -H "X-API-Key: eldb_live_…"

{
  "domain": "probikeshop.fr",
  "platform": "…",
  "merchantName": "…",
  "providers": ["dropcontact"],
  "decisionMakers": [{ "firstName": "…", "lastName": "…", "role": "president" }],
  "results": [
    {
      "firstName": "…", "lastName": "…", "role": "president", "provider": "dropcontact",
      "status": "found", "skipReason": null,
      "email": "…", "emailStatus": "valid", "emailStatusRaw": "nominative@pro", "emailKind": "professional",
      "phone": null, "phoneType": null, "linkedinUrl": "…", "jobTitle": "…",
      "creditsUsed": 1, "errorCode": null, "enrichedAt": "…", "reused": false
    }
  ]
}

Each result holds firstName, lastName, role, provider, status (pending, found, not_found, skipped, error), skipReason, email, emailStatus (valid, risky, catch_all, invalid, unknown), emailStatusRaw (the provider's own label), emailKind (professional, generic, personal), phone, phoneType, linkedinUrl, jobTitle, creditsUsed, errorCode, enrichedAt and reused (true: a result already found, no new credits).

GET /v1/stores/:domain also returns enrichment: your own results for this store, next to decisionMakers. Empty array if you have none.

Quotas and costs

Each plan has two counters: requests and stores. Both reset each time your subscription renews.

We email you when you have used 80% of a counter.

The store budget counts every store returned by /v1/stores/search, and 1 store per /v1/stores/:domain call. /v1/stores/count and /v1/me use no stores.

404, 403 and 409 errors are not billed.

EndpointRequestsStores
/v1/sample00
/v1/me00
/v1/stores/count10
/v1/stores/search11 per store returned
/v1/stores/:domain11
/v1/stores/:domain/catalog100
/v1/stores/:domain/trend20
/v1/enrichment/providers00
POST /v1/enrichments11 per store with decision makers
/v1/enrichments/:jobId00
/v1/stores/:domain/enrichment11

Response headers

Errors

An error returns an HTTP status and a JSON body with an error field.

HTTPCodeMeaning
400invalid_parameterA parameter is invalid, e.g. limit outside 1 to 100.
401missing_api_keyNo key was sent.
401invalid_api_keyUnknown or revoked key.
402active_subscription_requiredNo active subscription on this account.
402plan_upgrade_requiredEndpoint not in your plan (reason: exports_not_in_plan). Catalog and trends need Pro or Scale.
403plan_restrictionPlatform outside your databases (reason: platform_locked). The allowed field lists your databases.
404not_foundUnknown domain.
404no_catalogNo catalog for this store.
404no_trendNo trend for this store.
409no_database_selectedPick your databases in your account first.
429quota_exceededRequest quota used up until resetAt.
429store_quota_exceededStore budget used up until resetAt.
429rate_limitedToo many calls in a short time. Wait for the Retry-After delay.
429platform_change_limitedDatabase changes are limited to 1 every 30 days.
400too_many_storesMore than 25 stores in one enrichment.
402provider_out_of_creditsYour account at the provider has no credits left.
404provider_not_availableThis provider is not open for enrichment.
409provider_key_missingNo key saved for this provider.
422provider_key_invalidThe provider refused your key. Replace it in your account.
422provider_plan_has_no_apiYour plan at the provider does not include API access.
429byok_open_jobs3 enrichments are already running. Wait for one to finish.
429byok_daily_cap500 people sent to this provider today. Try again tomorrow.
429provider_rate_limitedThe provider asks to slow down. The job resumes on its own.
502provider_unavailableThe provider is not responding. The job retries on its own.
503opt_out_unavailableA pause of a few minutes: the objection check is not responding. Nothing is sent.

Example response when the store budget is used up:

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 25000
X-RateLimit-Remaining: 0
X-Stores-Limit: 10000
X-Stores-Remaining: 0

{
  "error": "store_quota_exceeded",
  "plan": "api_pro",
  "quotaMonthly": 25000,
  "quotaUsed": …,
  "storesMonthly": 10000,
  "storesUsed": 10000,
  "resetAt": "…"
}

MCP server

Connect your AI agent (Claude, Cursor…) to the database. Same key, same quotas. The server offers 8 tools:

Claude Code

claude mcp add eldb --transport http https://mcp.ecommerce-leads-database.com/mcp \
  --header "X-API-Key: eldb_live_…"

Cursor and compatible clients (JSON)

For a client that accepts a remote HTTP server with headers, such as Cursor (mcp.json):

{
  "mcpServers": {
    "eldb": {
      "url": "https://mcp.ecommerce-leads-database.com/mcp",
      "headers": {
        "Authorization": "Bearer eldb_live_…"
      }
    }
  }
}

Claude Desktop: use the mcp-remote bridge, in claude_desktop_config.json:

{
  "mcpServers": {
    "eldb": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote", "https://mcp.ecommerce-leads-database.com/mcp",
        "--header", "Authorization: Bearer eldb_live_…"
      ]
    }
  }
}

The key always goes in a header. Never put it in the URL.

Code examples

List every Shopify store with a Google profile, page by page.

JavaScript (fetch)

const API = "https://api.ecommerce-leads-database.com";
const headers = { "X-API-Key": process.env.ELDB_API_KEY };

let cursor = null;
do {
  const url = new URL("/v1/stores/search", API);
  url.searchParams.set("platform", "shopify");
  url.searchParams.set("hasGmb", "true");
  url.searchParams.set("limit", "50");
  if (cursor) url.searchParams.set("cursor", cursor);

  const res = await fetch(url, { headers });
  if (res.status === 429) {
    console.log(await res.json()); // quota or budget used up: see resetAt
    break;
  }
  if (!res.ok) throw new Error(`HTTP ${res.status}`);

  const { items, nextCursor } = await res.json();
  for (const store of items) console.log(store.domain);
  console.log("Stores left:", res.headers.get("X-Stores-Remaining"));
  cursor = nextCursor;
} while (cursor);

Python (requests)

import os
import requests

API = "https://api.ecommerce-leads-database.com"
headers = {"X-API-Key": os.environ["ELDB_API_KEY"]}
params = {"platform": "shopify", "hasGmb": "true", "limit": 50}

while True:
    r = requests.get(f"{API}/v1/stores/search", headers=headers, params=params)
    if r.status_code == 429:
        print(r.json())  # quota or budget used up: see resetAt
        break
    r.raise_for_status()
    data = r.json()
    for store in data["items"]:
        print(store["domain"])
    print("Stores left:", r.headers.get("X-Stores-Remaining"))
    if not data["nextCursor"]:
        break
    params["cursor"] = data["nextCursor"]