# AlertFlag API v1.1

REST API for UK company information: company profiles, financial data, officers, persons with significant control, charges, filing history, insolvency, recommended credit limits, company and director search, and company monitoring with alerts. All responses are JSON.

- Base URL: `https://console.alertflag.com/api/v1`
- This document: https://console.alertflag.com/api/v1/docs.md
- OpenAPI 3.0 description: https://console.alertflag.com/api/v1/openapi.json
- Web version: https://console.alertflag.com/api_reference.php

## Authentication

Every endpoint needs an API key. Keys are created by the account holder at https://console.alertflag.com/api_documentation.php (Professional and Enterprise plans) and look like `af_live_` followed by 48 hex characters. A key is shown once, when it is created.

Send the key in a header, over HTTPS:

```
X-API-Key: YOUR_API_KEY
```

or as a Bearer token:

```
Authorization: Bearer YOUR_API_KEY
```

Example:

```bash
curl https://console.alertflag.com/api/v1/company/00000000 \
     -H "X-API-Key: $ALERTFLAG_API_KEY"
```

Keeping the key safe:

- Call the API from your server. A key placed in a web page or mobile app can be read by anyone.
- Keep the key in an environment variable or secrets manager, not in source code.
- Send it in a header. The `api_key` URL parameter still works for older integrations but is deprecated, because URLs end up in logs.
- Rotate the key if a team member leaves or you think it has been exposed.

If you are an AI agent: read the key from the `ALERTFLAG_API_KEY` environment variable or the secret store you were given. Do not ask the user to paste it into the conversation, and do not write it into files, logs or output.

## Conventions

- A successful response is `{"success": true, "message": "...", "data": {...}, "timestamp": "..."}`. The result is always under `data`.
- A failed response is `{"success": false, "message": "...", "code": "...", "request_id": "...", "timestamp": "..."}`, sometimes with `details`. See Errors.
- Timestamps are UK time (Europe/London), formatted `YYYY-MM-DD HH:MM:SS`.
- Company numbers are 8 characters, for example `00445790` or `SC123456`. Shorter all-digit numbers are zero-padded for you.
- Monitoring and alerts use the same list as the Monitored Companies page, so a company added through the API appears in the console and is checked in the daily run. To collect new alerts, keep the highest `id` you have seen and pass it as `after_id` on the next call.
- Paged endpoints take `limit` and `offset`. Counts named `total_results`, `total_filings` or `total_charges` are the number of items in that response, not the size of the full list: keep paging until fewer than `limit` items come back.
- Company, officer, charge and filing records keep the field names Companies House uses.
- The API covers UK companies.

## Rate limits

The daily allowance depends on the plan:

- Professional: 100 requests per day
- Enterprise: 1,000 requests per day

The allowance is per account and resets at midnight UK time. Every authenticated response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (a Unix timestamp). Once the allowance is used the API answers `429` with a `Retry-After` header giving the seconds until the reset. `/usage` and `/info` are free to call at any time; every other call uses one request, including calls that end in an error.

Before a batch of calls, check `/usage` and stop when `requests_remaining` reaches 0.

## Endpoints

| Method | Path | Purpose |
|---|---|---|
| GET | `/company/{company_number}` | Get company profile information |
| GET | `/company/{company_number}/financial` | Get financial data and accounts |
| GET | `/company/{company_number}/officers` | Get list of officers/directors |
| GET | `/company/{company_number}/pscs` | Get persons with significant control |
| GET | `/company/{company_number}/charges` | Get charges and mortgages |
| GET | `/company/{company_number}/filing-history` | Get filing history, newest first |
| GET | `/company/{company_number}/insolvency` | Get insolvency cases, practitioners and dates |
| GET | `/company/{company_number}/credit-limit` | Get the recommended credit limit |
| GET | `/search/companies` | Search for companies |
| GET | `/search/officers` | Search for officers/directors |
| GET | `/search/disqualified-officers` | Search the disqualified directors register |
| GET | `/monitoring` | List the companies you monitor |
| POST | `/monitoring` | Start monitoring a company |
| DELETE | `/monitoring/{company_number}` | Stop monitoring a company |
| GET | `/alerts` | List alerts on your monitored companies |
| GET | `/usage` | Get your plan, daily allowance and requests used |
| GET | `/info` | List the endpoints |

## Company

### GET /company/{company_number}

Get company profile information. Uses one request from the daily allowance.

| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
| `company_number` | path | yes | string | UK company number, 8 characters (`00445790`, `SC123456`). Shorter all-digit numbers are zero-padded. |

Returns in `data`: The Companies House company profile (`company_name`, `company_status`, `type`, `date_of_creation`, `registered_office_address`, `sic_codes`, `accounts`, `confirmation_statement` and so on) plus `is_active` (boolean).

Response:

```json
{
    "success": true,
    "message": "Company profile retrieved successfully",
    "data": {
        "company_number": "00000000",
        "company_name": "EXAMPLE LIMITED",
        "company_status": "active",
        "date_of_creation": "2020-01-01",
        "type": "ltd",
        "registered_office_address": {
            "address_line_1": "123 Example Street",
            "locality": "London",
            "postal_code": "SW1A 1AA",
            "country": "United Kingdom"
        },
        "sic_codes": ["62012"],
        "is_active": true
    },
    "timestamp": "2026-11-10 12:00:00"
}
```

### GET /company/{company_number}/financial

Get financial data and accounts. Uses one request from the daily allowance.

| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
| `company_number` | path | yes | string | UK company number, 8 characters (`00445790`, `SC123456`). Shorter all-digit numbers are zero-padded. |

Returns in `data`: `summary` (latest year: `revenue`, `profit_before_tax`, `total_assets`, `shareholders_funds`, `current_ratio`) and `yearly`, keyed by year, each with `financial_metrics`. Figures are in pounds; a metric the accounts do not report is `null`. Answers 404 `no_financial_data` when no accounts can be read.

Response:

```json
{
    "success": true,
    "message": "Financial data retrieved successfully",
    "data": {
        "summary": {
            "latest_year": "2025",
            "revenue": 1500000,
            "profit_before_tax": 250000,
            "total_assets": 2000000,
            "shareholders_funds": 1200000,
            "current_ratio": 1.8
        },
        "yearly": {
            "2025": {
                "financial_metrics": {
                    "revenue": 1500000,
                    "profit_before_tax": 250000,
                    "total_assets": 2000000,
                    "current_assets": 800000,
                    "current_liabilities": 444444,
                    "shareholders_funds": 1200000
                }
            }
        }
    },
    "timestamp": "2026-11-10 12:00:00"
}
```

### GET /company/{company_number}/officers

Get list of officers/directors. Uses one request from the daily allowance.

| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
| `company_number` | path | yes | string | UK company number, 8 characters (`00445790`, `SC123456`). Shorter all-digit numbers are zero-padded. |

Returns in `data`: `summary` (`total_officers`, `active_officers`, `resigned_officers`) and `officers`: every current and resigned officer as Companies House records them (`name`, `officer_role`, `appointed_on`, `resigned_on`, `date_of_birth`, `address` and so on) plus `is_active`.

### GET /company/{company_number}/pscs

Get persons with significant control. Uses one request from the daily allowance.

| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
| `company_number` | path | yes | string | UK company number, 8 characters (`00445790`, `SC123456`). Shorter all-digit numbers are zero-padded. |

Returns in `data`: `summary` (`total_pscs`, `active_pscs`) and `pscs`: each person or entity with `name`, `kind`, `natures_of_control` (array), `notified_on`, `ceased_on` and `is_active`.

### GET /company/{company_number}/charges

Get charges and mortgages. Optional `limit` (max 100), `offset`. Uses one request from the daily allowance.

| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
| `company_number` | path | yes | string | UK company number, 8 characters (`00445790`, `SC123456`). Shorter all-digit numbers are zero-padded. |
| `limit` | query | no | integer | Items to return. Default 50, max 100. |
| `offset` | query | no | integer | Items to skip. Default 0. |

Returns in `data`: `summary` (`total_charges`, `outstanding_charges`, `satisfied_charges`, counted over the charges in this response) and `charges` as Companies House records them (`status`, `created_on`, `delivered_on`, `persons_entitled`, `particulars` and so on).

### GET /company/{company_number}/filing-history

Get filing history, newest first. Optional `limit` (default 25, max 100), `offset`. Uses one request from the daily allowance.

| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
| `company_number` | path | yes | string | UK company number, 8 characters (`00445790`, `SC123456`). Shorter all-digit numbers are zero-padded. |
| `limit` | query | no | integer | Items to return. Default 25, max 100. |
| `offset` | query | no | integer | Items to skip. Default 0. |

Returns in `data`: `summary` (`total_filings` in this response, `limit`, `offset`) and `filings` as Companies House records them (`date`, `type`, `category`, `description`). To read further back, raise `offset` by `limit` until fewer than `limit` filings come back.

### GET /company/{company_number}/insolvency

Get insolvency cases, practitioners and dates. Uses one request from the daily allowance.

| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
| `company_number` | path | yes | string | UK company number, 8 characters (`00445790`, `SC123456`). Shorter all-digit numbers are zero-padded. |

Returns in `data`: `has_insolvency_history` (boolean), `total_cases`, `status` and `cases` (each with `type`, `dates` and `practitioners`). A company with no insolvency history answers 200 with `has_insolvency_history: false`, not 404.

### GET /company/{company_number}/credit-limit

Get the recommended credit limit, the same figure as the Company Details page. Active companies only; can take over a minute the first time for accounts not filed digitally. Uses one request from the daily allowance.

| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
| `company_number` | path | yes | string | UK company number, 8 characters (`00445790`, `SC123456`). Shorter all-digit numbers are zero-padded. |

Returns in `data`: `credit_limit` (pounds, integer), `formatted_limit`, `assessment`, `calculation_date`, `basis`, `capped`, `parent_company`, `warning`, `note` and `factors_considered`. Allow up to 180 seconds for the response.

Response:

```json
{
    "success": true,
    "message": "Credit limit calculated successfully",
    "data": {
        "credit_limit": 42000,
        "formatted_limit": "£42,000",
        "assessment": "Medium Credit Capacity",
        "calculation_date": "2026-11-10",
        "basis": "model",
        "capped": false,
        "parent_company": null,
        "warning": null,
        "note": null,
        "factors_considered": {
            "net_worth": 400000,
            "total_assets": 900000,
            "current_ratio": 1.6
        }
    },
    "timestamp": "2026-11-10 12:00:00"
}
```

`basis` says how the figure was reached: `model` (our AI model on digitally filed accounts), `accounts_estimate` (accounts not filed digitally), `parent_company` (supported by a parent, named in `parent_company`) or `base` (no accounts filed yet, £500). `capped` is true at the £50M ceiling. `warning` carries any overdue-filing or strike-off caution shown in the console. Dormant and non-trading companies answer `not_scored`.

## Search

### GET /search/companies

Search for companies. Optional `limit` (default 20, max 50), `offset`. Uses one request from the daily allowance.

| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
| `q` | query | yes | string | Company name or number, at least 2 characters. |
| `limit` | query | no | integer | Items to return. Default 20, max 50. |
| `offset` | query | no | integer | Items to skip. Default 0. |

Returns in `data`: `query`, `total_results` (the number in this response) and `companies`, each with `company_number`, `title` (the name), `company_status`, `company_type`, `date_of_creation` and `address_snippet`.

### GET /search/officers

Search for officers/directors. Optional `postcode`, `birth_month`, `birth_year`. Uses one request from the daily allowance.

| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
| `forename` | query | no | string | First name. At least one of `forename` and `surname` is required. |
| `surname` | query | no | string | Last name. |
| `postcode` | query | no | string | Postcode of the person. |
| `birth_month` | query | no | integer | Month of birth, 1 to 12. |
| `birth_year` | query | no | integer | Year of birth, four digits. |

Returns in `data`: `search_criteria`, `total_results` and `officers`: one entry per person, with the companies they are or were an officer of.

### GET /search/disqualified-officers

Search the disqualified directors register. Optional `limit` (default 20, max 100), `offset`. Uses one request from the daily allowance.

| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
| `q` | query | yes | string | Name, at least 2 characters. |
| `limit` | query | no | integer | Items to return. Default 20, max 100. |
| `offset` | query | no | integer | Items to skip. Default 0. |

Returns in `data`: `query`, `total_results` (the number in this response) and `officers`, each with `title` (the name), `date_of_birth`, `address_snippet` and a `description` of the disqualification. A name match is not proof of identity: check the date of birth.

## Monitoring

### GET /monitoring

List the companies you monitor. Optional `limit` (default 50, max 200), `offset`. Uses one request from the daily allowance.

| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
| `limit` | query | no | integer | Items to return. Default 50, max 200. |
| `offset` | query | no | integer | Items to skip. Default 0. |

Returns in `data`: `total` (all monitored companies on the account), `limit`, `offset` and `companies`, each with `company_number`, `company_name`, `notes`, `monitor_since` and `last_check`.

### POST /monitoring

Start monitoring a company. JSON body: `company_number`, optional `notes`. Uses one request from the daily allowance.

| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
| `company_number` | JSON body | yes | string | UK company number. |
| `notes` | JSON body | no | string | Your own note, up to 2,000 characters. |

Returns in `data`: `company_number`, `company_name` and `already_monitored`. Answers 201 when the company is added and 200 when it was already monitored (sending `notes` then updates the note), so the call is safe to repeat. Answers 404 when the company does not exist.

Request:

```bash
curl -X POST https://console.alertflag.com/api/v1/monitoring \
     -H "X-API-Key: $ALERTFLAG_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"company_number": "00000000", "notes": "Key supplier"}'
```

Response:

```json
{
    "success": true,
    "message": "Company added to monitoring",
    "data": {
        "company_number": "00000000",
        "company_name": "EXAMPLE LIMITED",
        "already_monitored": false
    },
    "timestamp": "2026-11-10 12:00:00"
}
```

### DELETE /monitoring/{company_number}

Stop monitoring a company. Uses one request from the daily allowance.

| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
| `company_number` | path | yes | string | UK company number, 8 characters (`00445790`, `SC123456`). Shorter all-digit numbers are zero-padded. |

Returns in `data`: `company_number`. Answers 404 when the company is not on your monitoring list.

### GET /alerts

List alerts on your monitored companies. Optional `unread=1`, `company_number`, `type`, `since`, `after_id`, `limit` (default 50, max 100), `offset`. Uses one request from the daily allowance.

| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
| `unread` | query | no | string | `1` to return unread alerts only. |
| `company_number` | query | no | string | Alerts for one company. |
| `type` | query | no | string | One alert type: `officer_change`, `filing`, `gazette_notice`, `charge`, `insolvency`, `financial`, `credit_score`, `confirmation_statement`, `accounts_overdue`, `court_judgment`, `disqualified_director`, `payment_note`. |
| `since` | query | no | string | Alerts created at or after this date or date-time, for example `2026-11-01` or `2026-11-01T09:00:00`. |
| `after_id` | query | no | integer | Alerts with an `id` above this one, oldest first. Use it to poll for new alerts. |
| `limit` | query | no | integer | Items to return. Default 50, max 100. |
| `offset` | query | no | integer | Items to skip. Default 0. |

Returns in `data`: `total_results` (the number in this response), `limit`, `offset` and `alerts`, each with `id`, `company_number`, `company_name`, `type`, `message`, `data` (extra detail, or `null`), `read` and `created_at`. Newest first, unless `after_id` is sent. Reading alerts through the API does not mark them as read.

Response:

```json
{
    "success": true,
    "message": "Alerts retrieved successfully",
    "data": {
        "total_results": 1,
        "limit": 50,
        "offset": 0,
        "alerts": [
            {
                "id": 1042,
                "company_number": "00000000",
                "company_name": "EXAMPLE LIMITED",
                "type": "charge",
                "message": "New charge registered",
                "data": null,
                "read": false,
                "created_at": "2026-11-10 06:15:00"
            }
        ]
    },
    "timestamp": "2026-11-10 12:00:00"
}
```

## Account

### GET /usage

Get your plan, daily allowance and requests used. Not counted against your allowance.

Returns in `data`: `plan`, `daily_requests`, `requests_today`, `requests_remaining` and `resets_at`.

Response:

```json
{
    "success": true,
    "message": "Usage retrieved successfully",
    "data": {
        "plan": "Professional",
        "daily_requests": 100,
        "requests_today": 12,
        "requests_remaining": 88,
        "resets_at": "2026-11-11 00:00:00"
    },
    "timestamp": "2026-11-10 12:00:00"
}
```

### GET /info

List the endpoints. Not counted against your allowance.

Returns in `data`: `version`, `endpoints`, `documentation` (links to this reference) and `rate_limit`.

## Errors

Check the HTTP status first, then `code` for the exact reason. Every response carries an `X-Request-Id` header, repeated as `request_id` in error bodies; quote it when contacting support.

```json
{
    "success": false,
    "message": "Company not found",
    "code": "not_found",
    "request_id": "9f2c1a7b4e6d8035",
    "timestamp": "2026-11-10 12:00:00"
}
```

| Status | Code | Meaning |
|---|---|---|
| 400 | `bad_request`, `invalid_company_number`, `invalid_query`, `invalid_json`, `company_not_active`, `not_scored`, `credit_limit_disabled`, `no_financial_data` | The request is malformed or cannot be answered for this company |
| 401 | `missing_api_key`, `invalid_api_key` | No key was sent, or the key is wrong, expired or revoked |
| 403 | `plan_required`, `account_inactive` | The account has no active Professional or Enterprise subscription, or is suspended |
| 404 | `not_found`, `unknown_endpoint`, `no_financial_data` | No such company, record or endpoint |
| 405 | `method_not_allowed` | The endpoint does not support that HTTP method |
| 429 | `rate_limit_exceeded` | Daily allowance used. `Retry-After` gives the seconds until it resets |
| 500 | `internal_error` | Something went wrong on our side |
| 502 / 503 | `upstream_error`, `service_unavailable` | A data source or the API is temporarily unavailable. Retry after a short wait |

What to do:

- `400`, `404`, `405`: do not retry the same request. Fix it, or treat it as the answer (for example `not_scored` means the company has no credit limit).
- `401`, `403`: stop and tell the user. The key or the subscription needs their attention.
- `429`: stop until the time in `Retry-After` has passed.
- `500`, `502`, `503`: retry after a short wait, a few times at most.

## Support

https://alertflag.com/contact
