# BolagsAPI Documentation

> Complete API reference for Swedish company data. REST API with JSON responses.

## Authentication

All API requests require authentication using Bearer tokens:

```
Authorization: Bearer YOUR_API_KEY
```

Get your API key from the [dashboard](https://bolagsapi.se/dashboard).

## Base URL

```
https://api.bolagsapi.se
```

## Core Endpoints

### GET /v1/me
Return safe account metadata for the customer that owns the current API key, current API key metadata, and current billing-period usage.

This endpoint requires bearer API-key authentication. Dashboard session-only account routes remain separate under `/v1/account`.

**Example:**
```bash
curl -X GET "https://api.bolagsapi.se/v1/me" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**Response:**
```json
{
  "customer": {
    "id": "cus_abc123",
    "email": "developer@example.com",
    "tier": "starter",
    "base_tier": "starter",
    "billing_interval": "monthly",
    "company_name": "Example AB",
    "company_orgnr": "YOUR_ORGNR"
  },
  "api_key": {
    "id": "key_abc123",
    "prefix": "sk_live_a",
    "name": "Production key"
  },
  "usage": {
    "period": "billing:2026-06-10",
    "period_start": "2026-06-10",
    "period_end": "2026-07-10",
    "total": 420,
    "limit": 5000,
    "remaining": 4580,
    "requests": { "company": 300, "search": 120 },
    "current_key": {
      "total": 120,
      "requests": { "company": 120 }
    }
  }
}
```

### GET /v1/company/{orgnr}
Retrieve detailed information about a company by organization number.

**Tier Access:**
- **Free**: Basic info (name, status, address, VAT, dates, tax_status, announcements)
- **Starter+**: Full response including credit_rating, website, linkedin, board statistics

**Parameters:**
- `orgnr` (required): 10-digit Swedish organization number (with or without hyphen)
- `include` (optional): Include additional data: 'financials', 'fi', 'prv' (Starter+)

**Tax status semantics:**
- `tax_status.active` is combined SCB activity (VAT OR F-tax OR employer), not VAT registration. `/validate` and batch validation use SCB activity too.
- `tax_status.vat`, `f_tax` and `employer` are separate nullable flags: true, false or unknown (`null`). Preserve unknown values.
- `vat_number` is only set when `tax_status.vat=true`; it is null for both false and unknown registration.
- Company `status=unknown` means registry status is unavailable, not deregistered.

Illustrative VAT excerpt: `{"vat_number":null,"tax_status":{"active":true,"f_tax":true,"vat":false,"employer":null}}`.

**Tax status fields:**
- `tax_status.f_tax`: F-skatt registration status when enriched
- `tax_status.f_tax_registered_date`: F-skatt registration start date when available from enrichment sources
- `tax_status.vat`: VAT/moms registration status when enriched
- `tax_status.vat_registered_date`: VAT/moms registration start date when available from enrichment sources
- `tax_status.employer`: Employer registration status when enriched

**Example:**
```bash
curl -X GET "https://api.bolagsapi.se/v1/company/5567037485?include=financials" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### GET /v1/validate/{orgnr}
Quickly validate an organization number and check if the company exists.

**Example:**
```bash
curl -X GET "https://api.bolagsapi.se/v1/validate/5567037485" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**Response:**
```json
{
  "orgnr": "5567037485",
  "valid": true,
  "checksum_ok": true,
  "exists": true,
  "active": true,
  "name": "Spotify AB",
  "org_form": "AB"
}
```

### POST /v1/validate/batch
Validate up to 100 organization numbers in a single request.

**Example:**
```bash
curl -X POST "https://api.bolagsapi.se/v1/validate/batch" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"orgnrs": ["5567037485", "1234567890"]}'
```

### GET /v1/search
Full-text search across Swedish companies with optional filters.

**Parameters:**
- `q` (required): Search query (min 2 characters)
- `city` (optional): Filter by city name
- `sni` (optional): Filter by SNI code
- `org_form` (optional): Filter by organization form (AB, HB, EK, etc.)
- `active_only` (optional): Only return active companies (default: true)
- `foreign_owned` (optional): Filter by foreign ownership (`true` = only foreign-owned, `false` = only Swedish-owned subsidiaries)
- `limit` (optional): Results per page (1-100, default: 10)
- `offset` (optional): Pagination offset (default: 0)

**Example:**
```bash
curl -X GET "https://api.bolagsapi.se/v1/search?q=tech&city=Stockholm&limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Search results include both `orgnr` (10 digits) and `orgnr_formatted` (standard display format, e.g. `556703-7485`).

### GET /v1/search/persons (Starter+)
Search for persons (board members, executives, auditors) by name.

**Parameters:**
- `q` (required): Person name to search for
- `limit` (optional): Results per page (1-100, default: 10)

### POST /v1/person/companies (Pro+)
Look up companies connected to a person through registered roles such as board member, CEO, signatory, or owner. Prefer the opaque `person_id` returned by `/v1/company/{orgnr}/persons`; it works without supplying a personnummer. You can also supply `personnummer` or `personnummer_hash` when available.

This endpoint is included from Pro and is separate from official beneficial ownership.

**Access and billing:**
- Requires Pro plan or higher
- Included from Pro, subject to normal monthly usage limits
- Raw personnummer is redacted from API request logs and cache keys
- `person_id` and hash lookups only use already indexed local data and never trigger an external person lookup

**Example:**
```bash
curl -X POST "https://api.bolagsapi.se/v1/person/companies" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"person_id": "per_1_QX5iLkMtMTIz"}'
```

Or use a resolved hash:

```bash
curl -X POST "https://api.bolagsapi.se/v1/person/companies" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"personnummer_hash": "fd9484eebfb4b7c34026534fdb6d4b53583d767d21c2bb5c0b5d6df7a96b63c1"}'
```

The response echoes `person_id` or `personnummer_hash` without a separate personnummer field. For a registered sole trader, `companies[].orgnr` is its full 12-digit personnummer or coordination number used as the business identifier. `personnummer_hash` can remain null while `person_id` is available.

A personnummer lookup can return `202` with `Retry-After` while processing. Stop polling on `503` (`person_lookup_failed`); a lookup still pending after five minutes without usable data returns this error. Neither 202 nor 503 is billed. Registered sole traders and other company engagements are combined in the same response. Available data is returned with HTTP 200, which uses normal API units. If `refreshing` is true, more engagements may arrive: follow `Retry-After`. A source refresh failure with usable data returns 200 with `data_status: "failed"`; stop polling then. Registered sole traders have role `proprietor` (Innehavare). `unresolved_company_count` reports source rows omitted because their company identifier could not be resolved; a positive count means an incomplete result, and null means coverage is unknown. If only unresolved rows and no registered businesses are available, the endpoint returns 503 instead of an empty success.

### POST /v1/persons/beneficial-of (Pro+)
Look up companies where a person is registered as official beneficial owner (verklig huvudman).

**Access and billing:**
- Requires Pro plan or higher
- Pro includes 10 official beneficial ownership calls per month
- Business includes 100 official beneficial ownership calls per month
- Overage costs 4 kr/call on Pro and 3 kr/call on Business

**Example:**
```bash
curl -X POST "https://api.bolagsapi.se/v1/persons/beneficial-of" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"personnummer": "19800101-1234"}'
```

## Company Details

### GET /v1/company/{orgnr}/signatory (Starter+)
Get signatory rules (firmateckning) for a company.

If no local snapshot exists, the endpoint returns `202 Accepted` with `Retry-After`. Poll the same URL until `200 OK`. Queued responses are not billed.

### GET /v1/company/{orgnr}/signatories (Starter+)
Get the persons authorized to sign for the company. It uses the same `202 Accepted` polling pattern on a cache miss.

### GET /v1/company/{orgnr}/persons (Pro+)
Get list of persons associated with the company (board members, executives, auditors).

Each person includes an opaque `person_id` when the source provides a stable identity key. Send that ID to `POST /v1/person/companies` to find the person's other company roles even when `personnummer_hash` is null.

If no local snapshot exists, the endpoint returns `202 Accepted`, `data_status: "queued"` or `"refreshing"`, and a `Retry-After` header. Poll the same URL after that delay until it returns `200 OK`. Queued `202` responses are not billed. Existing stale data is returned with `200 OK` while it refreshes in the background.

### GET /v1/company/{orgnr}/ownership (Pro+)
Get ownership information including list of owners.

The same `202 Accepted` and `Retry-After` polling pattern applies on a cache miss. Queued responses are not billed.

### GET /v1/company/{orgnr}/contact (Starter+)
Get contact information (website, LinkedIn, phone, email).

The same `202 Accepted` and `Retry-After` polling pattern applies on a cache miss. Queued responses are not billed.

### GET /v1/company/{orgnr}/health (Pro+)
Get a 0-100 health score based on company status, tax registration, age, and risk factors.

### GET /v1/company/{orgnr}/timeline (Starter+)
Get chronological list of company events.

### GET /v1/company/{orgnr}/similar (Starter+)
Find companies with matching SNI codes in the same area.

### GET /v1/company/{orgnr}/credit (Starter+)
Get dedicated credit rating and risk score data.

### GET /v1/company/{orgnr}/bankruptcy (Starter+)
Get latest bankruptcy status and announcements.

### GET /v1/company/{orgnr}/creditor-call (Starter+)
Get latest creditor call (borgenärsutrop) information.

## Corporate Group

### POST /v1/vat/validate (Starter+)
Live EU VIES VAT validation for the caller's intra-EU trade. Send one JSON body:

```json
{"country_code":"SE","vat_number":"556016068001"}
```

Use the national VAT number without country prefix. VIES supports EU prefixes (EL for Greece) and XI for Northern Ireland; not GB. Spaces/hyphens are removed, leading zeroes preserved. Optional `requester: {"country_code":"SE","vat_number":"YOUR_OWN_NUMBER"}` requests a VIES consultation identifier where available.

A completed check returns HTTP 200 with `status: valid|invalid`, boolean `valid`, normalized input, nullable `name`/`address`, `checked_at`, source `request_date`, nullable `request_identifier`, requester context, `cached: false` and `meta.source: vies`. Undisclosed names/addresses are null, not mismatches. This does not perform approximate name matching.

VIES errors can arrive upstream with HTTP 200; BolagsAPI detects them. Outages/timeouts/load limits return HTTP 503 and `valid: null`, never false. Malformed input or requester rejection returns 400. Follow `Retry-After` for temporary failures. Each successful response, including cached or invalid results, uses three enrichment units; errors do not consume units.

Results are cached for 24 hours per customer, VAT number and requester. `cached` indicates a cache hit; `checked_at`, `request_date` and `request_identifier` retain the original check values. Errors are not cached. Swedish `tax_status`, organisation-number validation and billing tax treatment remain separate: VIES validity alone does not determine VAT exemption.

See [OpenAPI](https://api.bolagsapi.se/openapi.json) and the [VIES service information](https://ec.europa.eu/taxation_customs/vies/#/technical-information).

### GET /v1/company/{orgnr}/lei (Pro+)
Retrieve LEI identities and international accounting-consolidating parents from GLEIF. Costs 3 enrichment units. Uses a daily local Golden Copy snapshot, without upstream calls during customer requests.

- Exact linkage: validated Swedish organisation number through Bolagsverket registration authority RA000544, GENERAL entities only. No name matching.
- `linkage.canonical_lei`: the sole current ACTIVE/ISSUED or ACTIVE/LAPSED identity; a sole historical identity is retained if no current identity exists. Multiple competing identities return `ambiguous` without choosing parents.
- `identities`: LEI, name, jurisdiction, nullable Swedish orgnr, entity status, LEI registration status and source dates. LAPSED does not mean the entity is inactive.
- `direct_parents` / `ultimate_parents`: ACTIVE/PUBLISHED accounting-consolidation relationships for the canonical LEI, with parent identity, source validation and periods. Foreign parents can have `orgnr: null`.
- `other_relationships`: other relationship states, including LAPSED. These are not silently treated as current published relationships.
- `reporting_exceptions`: source categories, all reasons and references. An empty parent list does not establish that a company has no parent or owner.
- `snapshot`: publication/import timestamps and `stale` after 48 hours without a new publication. Individual records can be older than the snapshot.

These relationships do not establish legal ownership, shareholding percentages or beneficial owners. BIC/ISIN mappings and global LEI search are not included. A known company without an exact LEI match returns 200 with `linkage.status: not_found`; an unknown company returns 404. Before the first successful import the endpoint returns 503.

Example: `/v1/company/5560297029/lei` links Hitachi Energy Sweden to accounting-consolidating parents in Switzerland and Japan. See [OpenAPI](https://api.bolagsapi.se/openapi.json) for the full response contract. Source: [GLEIF Golden Copy](https://www.gleif.org/en/lei-data/gleif-golden-copy), [CC0 terms](https://www.gleif.org/en/meta/lei-data-terms-of-use).

### GET /v1/company/{orgnr}/group (Pro+)
Get Swedish corporate group structure (koncernstruktur), plus the independent `international_relationships` GLEIF object described above. Existing Swedish fields and `/group/members` retain their original meaning. The GLEIF section reports `status: unavailable` if no local snapshot exists.

### GET /v1/company/{orgnr}/group/members (Pro+)
Get all companies in the same corporate group.

## Annual Reports

### GET /v1/company/{orgnr}/reports
List available annual reports for a company with pagination.

**Parameters:**
- `limit` (optional): Number of reports to return (1-100, default 10)
- `offset` (optional): Starting position for pagination (default 0)

**Response includes:**
- `reports`: Array of report objects
- `total`: Total number of reports available
- `pagination.total`: Total reports count
- `pagination.returned`: Number of reports in current response
- `pagination.offset`: Current offset position
- `pagination.has_more`: Boolean indicating if more reports exist

### GET /v1/company/{orgnr}/reports/{id}
Download a specific annual report. PDF is the default. The API authorizes the request and redirects to a short-lived signed download URL.

**Parameters:**
- `format` (optional): 'pdf' (default) or 'raw' for original iXBRL when available

### GET /v1/company/{orgnr}/reports/{id}/auditor
Download the auditor's report (Revisionsberättelse).

## Financial Data (Starter+)

### GET /v1/company/{orgnr}/financials
Get multi-year financial data parsed from annual reports.

**Parameters:**
- `scope` (optional): `company` for the legal entity, `group` for consolidated figures, or `all` for every available accounting basis. If omitted, BolagsAPI selects one coherent series and prefers company figures.
- `include_comparative` (optional): Set to `true` to include comparison columns repeated from later annual reports.

Every row includes `accounting_scope`, `quality_status`,
`extraction_schema_version`, `is_primary`, and `is_comparative`.
`is_primary` describes the row's period in its source report; it does not mean
company rather than group figures. Public responses never include quarantined
rows. Current projections are `unverified`, including legacy rows formerly marked
`verified`. `verified` is reserved for documented validation with retained evidence;
automatic ingestion is not certification. `period_precision` is `month` or `day`
on `years[]` and `/financials/latest`; `fiscal_year_end` can be `YYYY-MM` or
`YYYY-MM-DD`. Do not invent a day. Month-only periods and broken fiscal years
remain valid. Invalid/future periods and non-SEK amounts are excluded from the
published financial series. Coarse Hitta turnover is `null` in exact revenue fields.

`years[].income_statement` includes nullable UC-style ranking fields:
`ebitda`, `result_after_financial_items`, and `personnel_costs`.
`result_after_financial_items` is an alias for `result_after_financial`.
K2 or condensed annual reports may return `null` when source lines are unavailable.

If no figures are stored yet, collection starts and all three financial endpoints
return HTTP 202 with `data_status` (`queued` or `refreshing`), `retry_after_seconds`
and `Retry-After`. Poll the same URL until HTTP 200; HTTP 202 is not billed.
HTTP 404 means no publishable figures are available after collection; HTTP 503 with
`data_status: "failed"` is a temporary collection error, so retry after `Retry-After`.

```bash
curl -X GET "https://api.bolagsapi.se/v1/company/5567037485/financials?scope=company&include_comparative=true" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### GET /v1/company/{orgnr}/financials/latest
Get only the most recent fiscal year's financial data.

Use the optional `scope=company`, `scope=group`, or `scope=all` parameter. The
response includes `accounting_scope`, `quality_status`, and
`extraction_schema_version`. Use `scope=company` when a decision must concern
the legal entity rather than its consolidated group.

### GET /v1/company/{orgnr}/financials/summary
Get trend data and growth metrics.

Use the optional `scope=company`, `scope=group`, or `scope=all` parameter. Every
trend point includes its own `accounting_scope` and `quality_status`. With
`scope=all`, the top-level scope can be `mixed`; growth is `null` when the
compared points use different accounting bases.

## Financial Health (Starter+)

### GET /v1/company/{orgnr}/financial-health

`model_version` identifies the shared API/batch calculation, currently `v2.3.0`.
Component scores and `composite_score` are indices from 0 to 10,000, not probabilities.
`ownership_score` uses equity and company age, not verified owners.

Illustrative response excerpt:
```json
{
  "model_version": "v2.3.0",
  "financial_flags": {
    "external_records_status": "not_covered",
    "public_defaults_count": null,
    "public_defaults_amount": null,
    "petitions_count": null,
    "petitions_amount": null
  }
}
```

These external registers are not covered. Display “Information unavailable”, not
“0 defaults”. Do not coerce `null` to zero. Legacy `/credit` returns the same
meaning under `risk_indicators`, with `payment_remarks_count` and
`payment_remarks_amount` instead of `public_defaults_*`. No new debt-register
or ownership integration is implied.

## AI Analysis (Pro+)

### GET /v1/company/{orgnr}/analysis
AI-generated credit rating and risk analysis.

**Parameters:**
- `lang` (optional): Language for summary (sv or en, default: sv)

### GET /v1/company/{orgnr}/valuation
Calculate indicative company valuation using multiple methods.

### GET /v1/company/{orgnr}/valuation/ai
AI-powered comprehensive valuation with detailed reasoning.

Analysis and both valuation endpoints return the same HTTP 202 (with `Retry-After`,
not billed) while financial figures are still being collected, and HTTP 503 for a
temporary collection error.

## Sanctions Screening (Starter+)

### GET /v1/screening/person/{name}

Screen a URL-encoded name against published EU, UN and OFAC sanctions and the
Swedish parliamentary person list (current and former MPs). This is not global
or Nordic PEP/RCA coverage. `min_score` is an integer 50–100 (default 80);
it measures name similarity, not identity probability. Existing aliases support
transliteration. `refresh=true` bypasses the name cache.

Each result includes `coverage.complete`, `datasetVersion`, `lists`, `truncated`,
`scope` and `limitations` inside `coverage`. Each list reports `source`, `status`
(`available`, `stale` or `missing`), `publishedAt` and `entries`. A list is stale
after seven days without successful publication. Candidates are ranked before
returning at most 100 entities per category; additional hits set `truncated=true`
and `complete=false`. Cache separates match thresholds and list versions.

### GET /v1/company/{orgnr}/screening

Screen the company name and available board/management names. Each nested name
result has the coverage object above. The top-level company coverage is different:

```json
{
  "overallRiskLevel": "none",
  "coverage": {
    "complete": false,
    "board": { "status": "missing", "observedAt": null, "completeness": "not_verified" },
    "beneficialOwners": { "status": "not_screened", "reason": "rvh_disabled" }
  }
}
```

This is an illustrative excerpt. A fresh board observation does not prove that
all representatives are known; observations older than 30 days are stale.
Beneficial owners are not screened. `refresh=true` bypasses nested name caches too.
No hits or `riskLevel=none` is not clearance; inspect coverage as well as matches.

### POST /v1/screening/batch

Send `{"names":["Exempelperson QZXV"],"min_score":80}` (maximum 50 names).
Every `results[]` item includes the full name-screening result and `coverage`.
`summary.incomplete` counts incomplete results. `summary.noRisk` counts only
results with no risk hits and complete coverage of the requested lists, not
cleared identities.

### GET /v1/screening/stats

Returns `totalSanctionsEntries`, `totalPepEntries`, `sanctionsBySource`,
`pepBySource` and `coverage` from successful list publications. Use these live
counts instead of fixed example totals. Retired entries remain in history and
are excluded from current matching. A failed import leaves the previous
publication intact.

## Supplier Check (Pro+)

### GET /v1/company/{orgnr}/supplier-check

Check a Swedish supplier against the versioned `se_supplier` rule set
(`rule_set_version` 1.0.0). Default rules: `registration_active`,
`no_insolvency_or_liquidation`, `f_tax_registered`, `vat_registered` and
`annual_report_current`. Optional: `auditor_opinion` and `beneficial_owners` via
`rules`, and financial thresholds `min_revenue`, `min_net_income`, `min_equity`
(SEK) and `min_equity_ratio` (percent). `accounting_scope` is `company` (default)
or `group`; `report_grace_days` is 0–365 (default 60).

Each rule returns `applicability` (`applicable`/`not_applicable`), `outcome`
(`pass`, `fail`, `unknown`; `null` when not applicable), a `reason` code,
`observed_at` and `evidence` (field, value, source category, period, scope,
currency, quality). `summary.outcome` is `fail` if any rule fails, `incomplete`
if none fails but any is unknown, and `pass` only when every applicable rule
passes. Unknown is never counted as a pass.

```json
{
  "rule_set_version": "1.0.0",
  "summary": { "outcome": "incomplete", "complete": false, "partial": true, "unknown_rules": ["f_tax_registered"] },
  "rules": [
    { "id": "f_tax_registered", "applicability": "applicable", "outcome": "unknown", "reason": "source_unavailable", "observed_at": null, "evidence": [] }
  ],
  "disabled_checks": [{ "id": "beneficial_owners", "status": "disabled", "reason": "integration_disabled" }]
}
```

This is an illustrative excerpt for a fictional company. F-tax and VAT use their
separate SCB registrations, not `tax_status.active`; unknown registration is not
deregistration, and an observation older than 30 days is `stale_observation`.
A report missing from BolagsAPI is not evidence of non-filing: `report_overdue`
requires a recorded Bolagsverket check after due date + grace, otherwise the rule
is `newer_report_not_collected`. Thresholds are compared with one stored period, scope and SEK;
quarantined, foreign-currency or other-scope figures give `unknown`, never a
fallback. Stored figures are unverified. `partial=true` means a source could not
be read; affected rules are unknown and `coverage` shows `unavailable`. Beneficial
owners are disabled and never passed. Restricted data returns `access_restricted`
without values. The same data, policy, date and rule version give the same result;
`reproducibility.facts_digest` identifies the inputs. One enrichment request,
also when partial. No AI is used.

## Industry Data (Pro+)

### GET /v1/industry/{sniCode}
Get statistics about companies in a specific SNI code industry.

### GET /v1/sni-codes
List all available SNI codes (Swedish Standard Industrial Classification).

**Parameters:**
- `level` (optional): Filter by hierarchy level (2=division, 3=group, 4=class, 5=subclass)
- `section` (optional): Filter by section letter (A-U)

**Headers:**
- `Accept-Language`: Language for descriptions (`sv` or `en`, default: `sv`)

**Example:**
```bash
curl -X GET "https://api.bolagsapi.se/v1/sni-codes?level=2&section=J" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**Response:**
```json
{
  "codes": [
    {
      "code": "62010",
      "description": "Dataprogrammering",
      "section": "J",
      "sectionName": "Informations- och kommunikationsverksamhet",
      "level": 5,
      "parentCode": "6201"
    }
  ],
  "total": 71
}
```

## Bulk Export

### POST /v1/export
Build company lists with a Business personal or team API key. Choose JSON or CSV and fields from the OpenAPI `ExportRequest` schema. `preview: true` returns exact `matched_rows`, up to 100 sample rows and coverage counts over all matches, without using export rows. Execution takes a new consistent snapshot and returns one row per organisation, up to 100,000 rows and 64 MiB. Narrow the selection when it exceeds those limits.

```json
{
  "filters": { "cities": ["Stockholm"], "org_forms": ["AB"], "employees_min": 5, "revenue_min": 10000000, "has_email": true },
  "fields": ["orgnr", "name", "city", "email", "phone", "website", "revenue", "net_income", "employees", "equity_ratio", "period_end", "accounting_scope", "quality_status"],
  "format": "json",
  "preview": false
}
```

Filters also support SNI codes/prefixes (all five industries), postal prefixes, counties, municipalities, organisation forms, status, registration dates, F-tax/VAT/employer flags, employee/revenue/net-income/equity-ratio min/max bounds, profitability, financial/contact availability, role mailboxes and exclusion of marketing-blocked companies or sole traders. Arrays are OR within a filter; filters combine with AND. Bounds are inclusive, money is whole SEK and ratios are percentages. Missing values remain null and do not satisfy financial/tax comparisons. `phone_blocked` is currently unknown (null).

Financial columns describe the latest selected financial period, using the same company/group selection as financial search. Exact money values are null for missing, quarantined, foreign-currency or interval-source observations. Include `period_end`, `accounting_scope`, `quality_status` and `financial_source` to identify the financial figures. Legacy verified flags are not published as verified.

Account restrictions apply to export fields and filters, including saved exports. Restricted fields or filters return 403; restricted categories are omitted from `coverage`.

Send a unique `Idempotency-Key` header per full export; reuse it with the same body, language and personal/team scope when retrying. Replays within 24 hours return the original `export_id` and rows without additional rows, API units or charges. Conflicting/expired keys return 409. Retry 429/503 with the same key and backoff; 429 includes `Retry-After`.

Each delivered row consumes 1 ordinary API unit from the payer's shared subscription allowance. Units beyond that allowance use the customer's basic overage rate and respect hard stops, spending caps and prepaid credit. Preview is free: `estimated_units` and `estimated_cost_sek` quote current usage and pricing, which may change before execution. `billing.mode` is `api_units`; `billing.units`, `overage_units` and `charged_sek` report the original delivery's units and additional cost excluding VAT. This cost is accrued for billing or deducted from prepaid credit, not a payment confirmation. `cost_sek` and `X-Export-Cost-SEK` mirror the additional cost for new exports. The 1,000,000-row monthly limit is a delivery limit, not an extra free allowance; `included_rows`, `remaining_rows` and `X-Export-Remaining` retain row-limit accounting. `X-Export-Units` and `X-Export-Overage-Units` expose units for CSV. Requests appear in Logs; each delivered row counts as one usage item and one ordinary unit. Replays retain original billing and remaining-quota metadata without charging again; older exports retain their original `included_rows` billing.

For an external checkout or list service, save the full returned dataset and calculate the final customer price from its actual row count before accepting payment. Later downloads use that saved file. Batch validation is for a supplied set of organisation numbers; export discovers companies matching filters.

## Webhooks (Pro+)

Each generated company-change delivery counts as one usage unit against the plan's included monthly usage. Usage above the included allowance costs 0.02 SEK per delivery. Automatic retries do not add usage. Detailed HTTP request and response headers/bodies are retained for 90 days; delivery status and timestamps remain available afterward.

### POST /v1/webhooks
Create a webhook subscription for company change notifications.

### GET /v1/webhooks
List all webhook subscriptions.

### PATCH /v1/webhooks/{id}
Update a webhook subscription.

### DELETE /v1/webhooks/{id}
Delete a webhook subscription.

### GET /v1/webhooks/{id}/deliveries
Get delivery history for a webhook.

### POST /v1/webhooks/{id}/test
Send a signed test webhook using the same JSON body shape and signature headers as normal webhook deliveries.

Successful test deliveries return a `delivery_id`. If your receiver returns a non-2xx response, BolagsAPI returns HTTP 400 with safe upstream diagnostics such as `webhook_id`, `delivery_id`, `upstream_status`, and `upstream_status_text`.

## Error Handling

The API returns standard HTTP status codes:

- `200` - Success
- `400` - Bad request (invalid parameters)
- `401` - Unauthorized (missing or invalid API key)
- `404` - Not found (company doesn't exist)
- `429` - Rate limit exceeded
- `500` - Server error

Error responses include a JSON body:
```json
{
  "error": "not_found",
  "message": "Company not found"
}
```

## Rate Limits

| Plan | Requests/minute |
|------|-----------------|
| Free | 10 |
| Starter | 60 |
| Pro | 200 |
| Business | 2,000 |

## Language Support

Set `Accept-Language` header to `sv` (default) or `en` for localized descriptions.

## Links

- [Pricing](https://bolagsapi.se/pricing)
- [Sign Up](https://bolagsapi.se/signup)
- [Dashboard](https://bolagsapi.se/dashboard)
- [Postman Collection](https://github.com/HugoAndFriends/BolagsAPI-API-dokumentation)
- [OpenAPI Spec](https://api.bolagsapi.se/openapi.json)

## Team workspaces

Select the team under Team in the dashboard header before creating a key under API keys. Team keys use the team owner's subscription, pricing, billing period and shared quota, regardless of who creates the key. Invited members do not need their own paid subscription. Owners and team admins manage keys; other members can view team usage and logs. You can also move an active personal REST key to a team under Dashboard → API keys → Move to team. You must be a team owner or admin. The API key stays the same; previous usage and charges stay on your personal account, and new requests use the team subscription. Managed OAuth/MCP connections remain personal. Send a team key using the same Authorization: Bearer header as any other API key; no team header is needed for public API requests.

The shared subscription quota includes the owner's personal keys and all teams owned by that account. Team usage and logs show only that team's activity. Revoking a key preserves usage history. Payment methods, overage settings and invoices are managed on the paying owner's account.
