---
title: "Get invoice stats per company"
method: GET
path: "/v1/accounts/{account_id}/companies/stats"
tags: ["AccountCompanies"]
---

# Get invoice stats per company

`GET /v1/accounts/{account_id}/companies/stats`

## Request

- Base URL: `https://app.beel.es/api`
- URL: `https://app.beel.es/api/v1/accounts/{account_id}/companies/stats`
- Auth: HTTP bearer

Returns, for each company of the account, how many fiscal documents it has
issued and when it last issued one. What counts is described on `invoice_count` and
`last_invoice_at` in the response.

**Paginated** with the usual `page`/`limit`, and the usual defaults: without them you get
the stats of the first 20 companies, not of all of them. One row per company, over the same
universe and in the same order as `GET /v1/accounts/{account_id}/companies` — `search`
included — so asking both with the same `page`, `limit` and `search` lines the two
responses up company by company.

## Query parameters

- `page` integer
- `limit` integer
- `search` string

## Response `200`

Per-company invoice stats

- ListCompanyStats200Response
  - `success` boolean
  - `data` object
    - `stats` CompanyStatsData[], required
      - `company_id` string, uuid, required
      - `invoice_count` integer, required: Fiscal documents issued by this company. Drafts, scheduled invoices and proformas never count, because there is no fiscal document yet. A rectifying invoice counts as a document of its own. An invoice voided with `POST /v1/companies/{company_id}/invoices/{invoice_id}/void` (issued by mistake) or a simplified invoice replaced by a standard invoice does not count, and neither does a rectifying invoice of an invoice voided that way. An invoice voided by a total rectifying invoice keeps counting, together with that rectifying invoice, so the pair counts as two documents. Not monotonic: voiding an invoice with that endpoint lowers this count, and rectifying an invoice for its full amount raises it by one document. Do not use it as a synchronisation cursor; webhooks and `GET /v1/accounts/{account_id}/request-logs` are the surfaces for that.
      - `last_invoice_at` string, date-time, nullable: Issue date of the most recent document in the same set as `invoice_count` (ISO 8601), or null if none. This is the issue date, not the accrual date. It moves *backwards* when the most recent document stops counting; see `invoice_count`.
    - `pagination` Pagination, required
      - `current_page` integer, required
      - `total_pages` integer, required: How many pages the query has. An empty collection has **one** page — the first one, empty — so the smallest value a canonical route returns is `1`. The deprecated flat aliases (`/v1/invoices`, `/v1/customers`, …) answer `0` for that same empty collection, and `has_next`/`has_previous` follow suit. They were left as they were on purpose: a client that reads `total_pages == 0` as "no results" would break if it changed under it. Do not compare the two families of routes field by field; read `total_items` if what you want to know is whether anything came back.
      - `total_items` integer, required
      - `items_per_page` integer, required
      - `has_next` boolean
      - `has_previous` boolean
  - `meta` ResponseMeta
    - `timestamp` string, date-time
    - `request_id` string

## Other responses

- `400`: The request target could not be read as declared, so nothing was looked up. Three causes, all named in `error.details`: a path or query parameter whose value does not parse as its type (`VALIDATION_ERROR` — a UUID that is not a UUID, an unknown enum value, an empty element in a list such as `status=ISSUED,`), a required parameter that was not sent (`MISSING_PARAMETER`), or a header that is meant to carry an identifier and does not (`ACTIVE_COMPANY_HEADER_INVALID`). A list parameter sent entirely empty (`status=`) is not an error: it is the same as omitting it. The fault is in the target, not in the content — which is what `422` is defined over.
- `401`: Missing or invalid authentication. Like every other error, `message`/`detail` is localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English when the header is missing or asks for none of those.
- `403`: The request does not resolve an account you reach (no active account, or the `BeeL-Active-Company` header points at a company you neither own nor manage).
- `422`: A parameter parsed as its declared type but its value is not one the operation admits: a `limit` outside its range (`limit=0`, `limit=101`), a `page` under 1, a `cursor` that is not one this API issued, more keys than the `metadata` filter accepts, a value outside the supported set. The query was rejected whole and nothing was read. Where a `400` says the target could not be understood, this says it was understood and refused. Fix the value — retrying the same query answers the same way.
- `429`: Rate limit exceeded. `RATE_LIMIT_EXCEEDED` when the request rate of your credential is over its limit; `TOO_MANY_REQUESTS` when an email the operation sends is held back because too many went out to that recipient in a short time. Either way, retry after the `Retry-After` seconds. `QUOTA_EXCEEDED` is the other `429`: the operation would go over a quota of your plan for the current period (issued invoices, for instance). Retrying does not help until the quota resets or the plan changes. `error.details` carries `feature_code`, `current_usage`, `quota_limit` and `reset_date`, and `RateLimit-Reset` the seconds until the reset when it is known.
- `500`: Internal server error
- `default`: Any status code the operation does not list above. Every operation declares it, so a generated client always has a branch to fall into and never loses the cause of a failure it did not anticipate. This is where the transport-level answers land — `405`, `406`, `415` and `429` — together with any status a future version of the API starts returning. All of them carry the same error envelope as the codes listed explicitly, so `error.code` is what tells them apart: switching on the status code alone is not enough. See «Transport-level errors» in the API description for when each one is produced. A `402` carrying `SUBSCRIPTION_REQUIRED` lands here when the account has no active subscription. `error.details` says why and what to do: `current_status` (absent if it never had one), `required_permission` and `action`. A `503` carrying `SERVICE_UNAVAILABLE` lands here too, with a `Retry-After` header: a dependency of BeeL itself (the database, the email provider) was momentarily unavailable and nothing was done. Retry after those seconds. A `502` carrying `EXTERNAL_SERVICE_ERROR` also lands here: an outbound integration the operation depends on failed or did not answer in time. It is a transient condition — retry with the same `Idempotency-Key` where the operation accepts one. One exception to the envelope: a failure of the network edge that never reaches the application (`502`, `503`, `504`, `524`) is generated by Cloudflare and its body is not BeeL's — it may not even be JSON. Treat those as "no answer", and retry.

---

[API](https://skmtc.dev/beel/apis/beel-public-api.md) · [All operations](https://skmtc.dev/beel/apis/beel-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/beel/apis/beel-public-api/revisions/ffbc0ae31f79?raw)
