New Monthly plans from $39/mo. No contracts, cancel anytime. See pricing →

API

Call the same data from your own code.

Getting started with the API

Everything the StackScan dashboard shows is available over a plain REST API: the technology stack behind any domain, the websites running a given technology, and the company behind a website.

Three minutes to your first call

  1. Create a token. In your dashboard, open My Account → API Tokens. Your workspace UUID is on the same page. The token is shown once.
  2. Send both on every request. The token as a bearer header, the workspace UUID as X-Tenant-Id.
  3. Call it. The base URL is https://api.stackscan.com.

One domain

curl https://api.stackscan.com/v1/tech-lookup/domains/lookup?domain=example.com \
  -H "Authorization: Bearer $STACKSCAN_TOKEN" \
  -H "X-Tenant-Id: $STACKSCAN_WORKSPACE"

A hundred at once

curl -X POST https://api.stackscan.com/v1/tech-lookup/companies/batch \
  -H "Authorization: Bearer $STACKSCAN_TOKEN" \
  -H "X-Tenant-Id: $STACKSCAN_WORKSPACE" \
  -H "Content-Type: application/json" \
  -d '{"domains":["shopify.com","stripe.com"]}'

The full API reference documents every parameter, with code samples in around twenty languages and a test console for each endpoint.

What you can call

Six endpoints. Every one returns JSON, and every one costs a single credit when it finds something.

Domain to technologies

Every technology detected on a domain, with its category and how many sites run it.

GET /v1/tech-lookup/domains/lookup

Technology to websites

Who runs a given technology, with totals and a country breakdown.

GET /v1/tech-lookup/technologies/lookup

Domain to company

The firmographics behind a website: name, industry, location, LinkedIn.

GET /v1/tech-lookup/companies/lookup

Companies, up to 100 at once

Resolve a whole page of companies in one request instead of one at a time. A 10,000-company CRM takes minutes, not hours.

POST /v1/tech-lookup/companies/batch

Technologies, up to 100 domains at once

Each domain's technology list in a single request, with an optional category filter.

POST /v1/tech-lookup/domains/batch

Check your balance

Remaining credits and this month's allocation. Free to call.

GET /v1/tech-lookup/credits

Other formats

Both batch endpoints also return CSV for spreadsheet workflows: add ?format=csv or send Accept: text/csv.

Building against this from an AI agent? There is a plain-text summary at llms.txt and a full field-level reference at llms-full.txt.

API credits and rate limits

The API draws on the same Bulk Lookup Credits as the dashboard, so there is nothing separate to buy.

One credit per hit

A lookup that finds nothing is not charged, and neither is checking your balance. In a batch you pay for the domains that resolved, not the ones you sent.

Rate limits

  • 120 requests a minute per token for single lookups.
  • 30 requests a minute for the batch endpoints, which is up to 3,000 domains a minute.

Reading your usage from the response

Every authenticated response carries headers you can read instead of guessing:

  • X-Credits-Used, X-Credits-Remaining, X-Credits-Cost
  • X-RateLimit-Limit, X-RateLimit-Remaining, Retry-After

Browser clients can read these too - they are listed in the API's CORS exposed headers.

Included in every plan

Any plan with Bulk Lookup Credits can use the API. See pricing for what each plan includes.

Technology lists (whole-technology exports)

A technology list is every website we know of running one technology. It is the same export the dashboard's Tech Lookup produces, requested over HTTP. It arrives as a file, and it lands in your Reports page exactly as if you had clicked the button yourself.

It costs one credit, whatever the size

A list of four million websites costs 1 credit. Not one per row, not one per domain. One.

Two things about that credit are worth knowing:

  • It comes out of your standard credits, not your bulk-lookup credits. These are separate balances. A full bulk-lookup balance will not pay for a list, and GET /v1/me reports both so you can check which is which.
  • Asking for the same technology again inside the same billing period is free. One credit buys that technology for the period, so a nightly refresh costs one credit a month rather than thirty.

Nothing is charged when a request is refused. An unknown technology, or filters that match no websites, both come back as an error with your balance untouched.

It is asynchronous, and it has to be

Building a list can take minutes, so there is nothing useful to return straight away. You start it, poll, then download.

1. Start it

curl -X POST https://api.stackscan.com/v1/tech-lookup/lists \
  -H "Authorization: Bearer $STACKSCAN_TOKEN" \
  -H "X-Tenant-Id: $STACKSCAN_WORKSPACE" \
  -H "Content-Type: application/json" \
  -d '{"technology": "Shopify"}'
{
  "success": true,
  "report_id": 241,
  "status": "queued",
  "technology": "Shopify",
  "credits_charged": 1,
  "credit_type": "standard",
  "reused_billing_period": false,
  "estimated_rows": 4210338,
  "status_url": "https://api.stackscan.com/v1/tech-lookup/lists/241"
}

reused_billing_period: true means you had already bought this technology this period and were not charged again.

2. Poll until it is ready

curl https://api.stackscan.com/v1/tech-lookup/lists/241 \
  -H "Authorization: Bearer $STACKSCAN_TOKEN" \
  -H "X-Tenant-Id: $STACKSCAN_WORKSPACE"

status is queued, ready or failed. Polling is free, so check every few seconds at first and back off. A failed list has already had its credit refunded automatically, so you do not need to ask.

3. Download

curl -L -o shopify.csv \
  https://api.stackscan.com/v1/tech-lookup/lists/241/download \
  -H "Authorization: Bearer $STACKSCAN_TOKEN" \
  -H "X-Tenant-Id: $STACKSCAN_WORKSPACE"

Large lists come back as CSV, small ones as XLSX. Downloading while the list is still building returns 409, so poll first.

Download limits

These files are big, so downloads are limited in two ways. Neither costs credits.

A rate limit of a few downloads a minute. Generous enough to retry a failed transfer, tight enough that a runaway script cannot pull terabytes. Exceeding it returns 429 with a retry_after, and waiting fixes it.

A ceiling of 30 downloads per list. The status response reports download_count, download_limit and downloads_remaining so you can see it coming. Past the ceiling you get 403 with error_code: download_limit_reached. Not a 429, because retrying will not help. Contact us if you need it lifted, or start a fresh list.

Two details worth knowing:

  • Attempts are counted, not completed downloads. A transfer that drops halfway still spends one. We hand the file to the web server and return immediately, so we genuinely cannot tell a completed download from an abandoned one. The ceiling is 30 rather than something smaller precisely because of that.
  • An expired file does not count. If retention has removed the file you get 410 and your remaining downloads are untouched.

If you find yourself near the ceiling regularly, you probably want to fetch once and cache it your side rather than re-downloading. The data only changes when we recrawl.

Check truncated before you treat a list as complete

Plans cap how many rows an export may contain, and there is a ceiling of 1,000,000 rows even on unlimited plans. Popular technologies exceed that.

The status response tells you exactly where you stand:

Field Meaning
result_count how many websites matched
exported_rows how many are actually in the file
truncated true when the cap cut the list short
withheld_rows the difference between the two

If truncated is true, the file is the top slice of a larger population, not the whole of it. Narrowing with filters is usually more useful than a truncated list of everything.

Narrowing the list

{
  "technology": "Shopify",
  "tld": [".co.uk"],
  "only_with_company_info": true
}
  • technology: the name. Use stack_id instead if you want to be exact.
  • country / country_condition: country IDs, include or exclude.
  • tld / tld_condition: domain endings, include or exclude.
  • company_size: restrict to company size bands.
  • only_with_company_info: skip websites we hold no company record for. Smaller list, far more useful for outreach.

Your list history

curl https://api.stackscan.com/v1/tech-lookup/lists \
  -H "Authorization: Bearer $STACKSCAN_TOKEN" \
  -H "X-Tenant-Id: $STACKSCAN_WORKSPACE"

Every list your workspace has requested, newest first. These are the same rows the dashboard shows under Reports. Lists belong to the workspace that paid for them, so any member can fetch one.

How long files are kept

Generated files are retained for a period and then removed; the report row stays. A download of an expired list returns 410 Gone, which is deliberately different from 404 so you can tell "this expired" from "this never existed". Request it again to rebuild it.