Metadata-Version: 2.5
Name: bormeapi
Version: 1.0.0
Summary: Python client for the BORME API: Spanish company registry data (BORME) over REST
Project-URL: Homepage, https://bormeapi.com/sdk
Project-URL: Documentation, https://api.bormeapi.com/docs
Author: BORME API
License: MIT
License-File: LICENSE
Keywords: api client,borme,company registry,kyb,registro mercantil,spain
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Typing :: Typed
Requires-Python: >=3.9
Provides-Extra: test
Requires-Dist: pytest>=7; extra == 'test'
Description-Content-Type: text/markdown

# bormeapi (Python)

Python client for [BORME API](https://bormeapi.com), an independent service
providing data derived from BORME notices and other public registries through a REST API. Standard library only, no runtime dependencies, Python 3.9 or later.

[Leer en español](https://bormeapi.com/es/sdk)

## Quick start

```python
from bormeapi import BormeClient
client = BormeClient("YOUR_API_KEY")   # or BormeClient() with BORME_API_KEY set
company = client.company_by_nif("A46103834")
print(company["company_name"], company["status"])
for event in client.iter_events(province="MADRID", act_type="INCORPORATION", max_items=20): print(event["date"], event["company_name"])
```

Get a free key at https://bormeapi.com/account. The free plan allows 50
requests a day and the last 7 days of history; some endpoints and filters need
a paid plan. The key travels in the
`X-API-Key` header only. Pass your key explicitly, or use `client = BormeClient()`
after setting `BORME_API_KEY`: the variable is read only when no key is passed.

The client accepts HTTPS URLs and allows HTTP only for the hosts `localhost`,
`127.0.0.1` and `::1`. Any other `base_url` raises `ValueError` when the client
is constructed.

## Install

```sh
pip install bormeapi
```

## What it covers

- Companies: `company(slug, as_of=, include_inferred=)`, `company_by_nif(nif)`,
  `companies_by_nif([...])` (up to 100), `companies_by_hoja(hoja)`,
  `search_companies(name, province=, status=, limit=, offset=)`,
  `company_events(slug, ...)`, `company_signals(slug, ...)`,
  `company_risk(slug)`, `company_network(slug, ...)`, `person_roles(name)`.
- EU VAT numbers: `vat_check(vat_number)` checks whether VIES reported the number as
  valid for intra-EU transactions on `request_date` (Basic plan or higher).
- Events: `events(**filters)` for one page, `iter_events(**filters)` for all
  pages, `events_csv(**filters)` for CSV bytes. Filters: `date_from`,
  `date_to`, `province`, `act_type`, `company_name_contains`, `capital_min`,
  `capital_max`, `municipality`.
- Signals: `signals(signal_type=, date_from=, date_to=, province=, ...)`.
- Trademarks and trading names: `search_brands(q)`, `company_brands(slug)`.
- Watchlists: `create_watchlist(webhook_url=, company_slug= | name_contains= | filter=)`,
  `list_watchlists()`, `delete_watchlist(id)`.
- Reference data: `act_types()`, `provinces()`, `sources()`, `health()`.
- Anything else: `request("GET", "/v1/...", params={...})`.

Company responses also include `lei` (the GLEIF Legal Entity Identifier found by
NIF) and `website` (the address as published in the BORME); the API reference
explains when they are null.

With `include_inferred=True`, `company()` and `company_events()` may also return a CNAE-2009
division inferred by a language model from the company's stated corporate
purpose (objeto social), when it meets the confidence threshold (0.9 by
default) and no CNAE code appears on the company card (for `company()`) or in
any of the event's acts (for `company_events()`) (`cnae_source` `jev`, details in `cnae_inference`). It describes the stated
purpose, not the company's verified activity, and is not a code published in
the BORME. If `include_inferred` is omitted or set to
`False`, no inferred values are included.

Responses are plain dicts typed with `TypedDict` (`bormeapi.models`), so new
fields the API adds reach you without an upgrade.

## Pagination

`iter_events` requests pages of up to 200 events and stops at the last page,
after `max_items`, or when the offset would pass 10,000 (the API limit). For
larger result sets, split the date range. Every page is one request and counts
against your quota.

## Errors and retries

HTTP error responses raise `bormeapi.ApiError` or one of its subclasses,
with `status`, `code` (the API error code when there is one), `message`, `body` and
`headers`:

- `AuthenticationError` (401), `PermissionDeniedError` (403),
  `NotFoundError` (404, with `suggestions` for an unknown slug),
  `ConflictError` (409, with `candidates` for an ambiguous NIF),
  `ValidationError` (422, with `errors`), `RateLimitError` (429),
  `ServiceUnavailableError` (503), `GatewayTimeoutError` (504).
- Network failures raise `TransportError`.

A 429 or 503 is retried up to three times (`max_retries=3`) only when
`Retry-After` is valid and asks for a wait of at most 60 seconds
(`max_retry_wait=60`); otherwise the error is raised at once. A 503 is retried
only for GET and DELETE. A 429 without `Retry-After` means the daily quota is
used up: it resets at 00:00 UTC, so it is raised at once, with `limit` and
`upgrade_url`.

```python
from bormeapi import NotFoundError
try:
    client.company("mercadona-sl")
except NotFoundError as e:
    print(e.message, e.suggestions)
```

Requests using an old slug for a renamed or merged company are redirected to the
current slug.
The client follows up to three redirects, only to the same origin, so the key
is never sent elsewhere.
