Swiss Data Atlas API

Everything the app can do is available over HTTPS: company search, contact reveals, industry monitors, digests, reference data and natural-language questions. Keys are scoped, metered and self-service.

Create a key in the app · OpenAPI schema

Authentication

Send your key as a bearer token on every request. Base URL: /api/public/v1.

curl -s https://swissdataatlas.com/api/public/v1/me \
  -H "Authorization: Bearer sda_live_…"

Prepaid billing, consumption and receipts

Billing calls require an organisation admin (or global admin), the indicated billing scope, and an organisationId query parameter. Existing keys do not automatically gain billing scopes. List calls support limit (1 to 100, default 50) and offset (default 0). Consumption is read from recorded credit movements, not API request counts or proof of cash payment.

GET /billing/consumption?organisationId=YOUR_ORGANISATION_UUID&limit=50&offset=0
POST /billing/topups?organisationId=YOUR_ORGANISATION_UUID
{ "amount": 100 }

GET /billing/receipts?organisationId=YOUR_ORGANISATION_UUID
GET /billing/receipts/RECEIPT_UUID?organisationId=YOUR_ORGANISATION_UUID&download=html

A topup request returns Wise bank details and a unique payment reference. It does not move money. Setup must be completed before requests can be created. Automatic Wise confirmation is not enabled. Once a global admin has independently verified the received CHF amount and transaction in Wise, confirmation credits the organisation pool exactly once. Organisation admins allocate member allowances separately. Pending transfers have no receipt. Receipts are printable HTML downloads, not Wise-issued PDFs or consumption invoices.

POST /billing/topups/TOPUP_UUID/confirm?organisationId=YOUR_ORGANISATION_UUID
{ "wiseTransactionId": "VERIFIED_WISE_TRANSACTION", "verifiedAmount": 100, "currency": "CHF" }

Confirmation requires verified seller address and UID / VAT details. Repeating the same confirmation is safe; reusing a Wise transaction for another topup is rejected. Contact sda@bi-concepts.ch for billing setup. Commercial terms are available under AGB, currently a draft for review.

Response format

Successful calls return data plus meta; failures return an error object with a stable code.

{
  "data": { "companies": [ … ] },
  "meta": {
    "requestId": "…",
    "generatedAt": "2026-01-01T12:00:00.000Z",
    "rowsReturned": 42,
    "quota": {
      "rowsUsedThisMonth": 1042,
      "rowsLimitPerMonth": 25000,
      "revealsUsedThisMonth": 3,
      "revealsLimitPerMonth": 25,
      "maxRowsPerRequest": 250
    }
  }
}

Every response also carries x-ratelimit-limit, x-quota-rows-limit/-used and x-quota-reveals-limit/-used.

Scopes

  • companies:readSearch companies and read company details
  • contacts:readReveal signatory contact details (billed per lookup)
  • monitors:readList saved monitors
  • monitors:writeCreate monitors and trigger runs
  • digests:readRead a monitor's current digest
  • ask:writeAsk questions in natural language
  • reference:readRead industry (NOGA) and canton reference data
  • customers:readRead your customer lists, their classification and gap analysis
  • customers:writeUpload customer lists for classification and delete them
  • billing:readRead consumption and download paid receipts (organisation admins only)
  • billing:writeRequest prepaid Wise topups (organisation admins only)

Endpoints

MethodPathScopeWhat it does
GET/billing/consumptionbilling:readPaginated organisation credit movements, including purchases and confirmed topups.
GET / POST/billing/topupsbilling:read / billing:writeList or request prepaid CHF bank transfers to Wise.
GET/billing/receiptsbilling:readList confirmed payment receipts.
GET/billing/receipts/{id}billing:readRead a receipt; ?download=html downloads a printable receipt.
POST/billing/topups/{id}/confirmbilling:writeGlobal admin only: confirm a payment independently verified in Wise.
GET/mereference:readKey scopes, limits and current usage.
POST/companies/searchcompanies:readSearch companies by industry, canton, registration window or anniversary.
GET/companies/{uid}companies:readOne company by Swiss UID (CHE-…) or registry id, with signatories.
POST/contacts/revealcontacts:readReveal a signatory's e-mail, phone and profiles. Billed per new lookup.
GET/monitorsmonitors:readList your saved monitors.
POST/monitorsmonitors:writeCreate a monitor.
POST/monitors/{id}/runmonitors:writeRun a monitor now and mail its digest.
GET/digests/{id}digests:readThe current digest of a monitor as JSON.
POST/askask:writeAsk a question in plain language; returns prose, SQL and rows.
POST/customers/listscustomers:writeUpload up to 5,000 customers (name, UID, town); returns the classification.
GET/customers/listscustomers:readYour customer lists and those shared in your organisation.
GET/customers/lists/{id}customers:readClassification summary and every matched customer.
GET/customers/lists/{id}/gap-analysiscustomers:readGap analysis: untapped active companies per industry. ?cantons=customers|all
DELETE/customers/lists/{id}customers:writeDelete a list you uploaded.
GET/reference/nogareference:readIndustry tree (NOGA 2025).
GET/reference/cantonsreference:readCantons and mutation types.

Example: company search

curl -s https://swissdataatlas.com/api/public/v1/companies/search \
  -H "Authorization: Bearer sda_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "anniversary",
    "anniversaryYears": [10, 25],
    "fromMonths": 3,
    "toMonths": 24,
    "cantons": ["ZH", "ZG"],
    "industries": [{ "level": "section", "code": "L" }],
    "websiteOnly": true,
    "limit": 100
  }'

Example: classify customers and run a gap analysis

Send your customers with a company name, a UID number (CHE-…) or both; the town helps when several companies share a name. Each row is matched against the commercial register and classified by industry (NOGA), canton, legal form and age. Rows come back as matched, ambiguous (several candidates, please check) or unmatched. Lists are shared with your organisation, just like in the app under My customers. Up to 5,000 rows per upload.

curl -s https://swissdataatlas.com/api/public/v1/customers/lists \
  -H "Authorization: Bearer sda_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Key accounts 2026",
    "customers": [
      { "name": "Muster AG", "uid": "CHE-123.456.789", "town": "Zürich" },
      { "name": "Beispiel GmbH", "town": "Luzern" }
    ],
    "includeItems": false
  }'
{
  "data": {
    "id": "8f0c…",
    "name": "Key accounts 2026",
    "classification": {
      "total": 2, "matched": 2, "ambiguous": 0, "unmatched": 0,
      "byIndustry": [{ "label": "68 Real Estate Activities", "count": 1 }, …],
      "byCanton":   [{ "label": "ZH", "count": 1 }, …],
      "byLegalForm": [ … ],
      "byAge": [{ "label": "10-19 years", "count": 1 }, …]
    }
  },
  "meta": { … }
}

Then ask where the untapped potential is. By default the comparison is limited to the cantons your customers are in; pass ?cantons=all for all of Switzerland.

curl -s "https://swissdataatlas.com/api/public/v1/customers/lists/8f0c…/gap-analysis?cantons=customers" \
  -H "Authorization: Bearer sda_live_…"

{
  "data": {
    "cantonScope": "customers",
    "cantons": ["LU", "ZH"],
    "industries": [
      { "divisionCode": "68", "divisionName": "Real Estate Activities", "sectionCode": "L",
        "customers": 12, "totalActiveCompanies": 4210, "untapped": 4198, "coverage": 0.0029 }
    ]
  }
}

Example: ask a question

curl -s https://swissdataatlas.com/api/public/v1/ask \
  -H "Authorization: Bearer sda_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "question": "How many companies were registered in Zug last month?" }'

Limits and errors

Each key has a per-request row cap, a monthly row allowance, a monthly contact-reveal allowance, a requests-per-minute limit and optional canton or industry restrictions. Error codes you should handle: missing_key, invalid_key, insufficient_scope, canton_not_allowed, rate_limited, row_quota_exhausted, reveal_quota_exhausted, not_found.