# Retrieve one article's performance **GET /articles/{article}/stats** How one issue did on both sides at once: what the email did, and what the community did with it afterwards. No email provider can return the right hand column, because the reading, highlighting and arguing happened on Commune. `email` is present only for an issue Commune sent itself. For an issue imported from an outside provider it is `null`, because that provider mailed it and never handed Commune the per recipient outcome. It is `null` for an issue that has not been sent yet as well. `community` is computed at read time and keeps counting long after the send, so two reads a week apart legitimately disagree. Creator scope. The public tallies on an article, which are `likes`, `comments` and `highlights`, live on the article itself and need no creator key. ## Servers - Production. There is no separate sandbox host. : https://api.usecommune.com (Production. There is no separate sandbox host. ) ## Authentication methods - Api key ## Parameters ### Headers - **Commune-Version** (string(date)) The contract version this request is written against, as a release date (`YYYY-MM-DD`). Omitting the header pins the request to the version that was current when the API key was issued, so an integration keeps working when a newer version ships. An unknown value answers `400` with `invalid_version`. ### Path parameters - **article** (string) The article's `id` (a UUID) or its `short_id`, an eight character base62 string that is unique across Commune. The `slug` is not accepted here because it is unique only within a newsletter. ### Query parameters - **expand** (string) Comma separated list of relationship paths to inline in the response. Unexpanded relationships are returned as a reference object carrying only `id` and `object`. Each operation documents the paths it accepts, and an unknown path answers `400`. Nested paths use a dot, for example `article.newsletter`. One accepted path is not a relationship. `?expand=content` on `GET /articles/{article}` adds the Markdown rendition of the body beside the HTML one. It is the same trade the parameter always offers, a fuller response for a larger one, over a property that has more than one representation rather than over a reference. - **fields** (string) Comma separated allow list of top level properties to return on each object, so a client can trim a response it does not need in full. `id` and `object` are always returned. An unknown property name answers `400`. Properties omitted by an operation, such as `content` on any article list, cannot be brought back with `fields`. A trimmed body is a subset of the schema this operation declares, and a property that schema marks required is absent when it was not asked for. That is the point of the parameter, so a client that validates responses against the schema either sends no `fields` or relaxes `required`. ## Responses ### 200 The article's email and community performance. #### Body: application/json (object) - **object** (string) Always `article_stats`. - **article** (object | null) The issue measured. A `Ref` unless `article` is named in `?expand=`. - **email** (object | null) What happened in the inbox, counted per recipient from Commune's own send records. `null` for an issue Commune did not send: one imported from an outside provider, which mailed it without handing Commune the outcome, and one that has not been sent yet. These are counts of recipients, not rates. Divide by `delivered` rather than by `recipients` to get the rates a provider quotes. - **community** (object) What happened on Commune. Computed at read time and still moving, so two reads a week apart legitimately disagree. ### 400 The request was malformed: an unknown query parameter, an unparseable cursor, an unknown `expand` path, or an unrecognised `Commune-Version`. #### Body: application/json (object) - **error** (object) ### 401 No API key was presented, or the key is unknown, revoked or expired. All four answer identically, down to the wording. Saying that a key was revoked rather than never issued confirms to whoever is holding the string that it was once real, which a legitimate caller does not need and a thief should not get. #### Headers - **WWW-Authenticate** (string) The authentication scheme this API accepts. Always `Bearer realm="Commune API"`; there is no second scheme and no query-parameter fallback, because a credential that can travel in a URL ends up in access logs and referer headers. #### Body: application/json (object) - **error** (object) ### 403 The key is valid but is not allowed to read this. Either it carries public scope and the operation needs creator scope, or it is bound to a different newsletter than the one addressed. #### Body: application/json (object) - **error** (object) ### 404 No such resource, or the key is not allowed to know that it exists. Commune answers `404` rather than `403` where distinguishing the two would leak the existence of private content. #### Body: application/json (object) - **error** (object) ### 429 Too many requests. Back off and retry after the interval named by the `Retry-After` response header. #### Headers - **Retry-After** (integer) Seconds to wait before retrying. #### Body: application/json (object) - **error** (object) ### 500 Something failed inside Commune. The request may be retried. #### Body: application/json (object) - **error** (object) [Powered by Bump.sh](https://bump.sh)