Skip to content

Every tag in use

GET
/api/tags

The site’s tag vocabulary — each tag’s slug, the title a reader reads, and how many documents carry it in each collection. A tag nothing carries is absent.

This is the list the grammar cannot stand in for. The collections are a closed set published in /api/query-schema.json, so a caller reading the grammar already knows them; tags are content, and no schema can enumerate them.

Narrow a query with the slug, not the title. The whole vocabulary comes back in one response unless ?limit names a bound; total reports the whole of it either way, so a bounded answer says what it left out.

This resource answers HEAD as well, with the same headers and no body.

It also answers OPTIONS, which returns Allow, a Link to this description, and — where the resource takes one — the query formats it reads, as Accept-Query.

Query Parameters

limit?integer

How many items to return. Omitted, the whole list comes back. A value that is not a whole number of at least 1 reads as no bound rather than as an empty page. total in the body says how many exist either way.

Response Body

application/json

application/problem+json

curl -X GET "https://example.com/api/tags"
{  "items": [    {      "slug": "string",      "title": "string",      "count": 0,      "byCollection": {        "writing": 0,        "glossary": 0,        "demos": 0      }    }  ],  "count": 0,  "total": 0}
---
title: "Every tag in use"
description: "The site’s tag vocabulary — each tag’s slug, the title a reader reads, and how many documents carry it in each collection. A tag nothing carries is absent.\n\nThis is the list the grammar cannot stand in for. The collections are a closed set published in [`/api/query-schema.json`](/api/query-schema.json), so a caller reading the grammar already knows them; tags are content, and no schema can enumerate them.\n\nNarrow a query with the **slug**, not the title. The whole vocabulary comes back in one response unless `?limit` names a bound; `total` reports the whole of it either way, so a bounded answer says what it left out.\n\nThis resource answers `HEAD` as well, with the same headers and no body.\n\nIt also answers `OPTIONS`, which returns `Allow`, a `Link` to this description, and — where the resource takes one — the query formats it reads, as `Accept-Query`."
url: "https://lateano.com/developers/api/reference/reading/tags.get"
author: "Colin Lateano"
---

# Every tag in use

The site’s tag vocabulary — each tag’s slug, the title a reader reads, and how many documents carry it in each collection. A tag nothing carries is absent.

This is the list the grammar cannot stand in for. The collections are a closed set published in [`/api/query-schema.json`](/api/query-schema.json), so a caller reading the grammar already knows them; tags are content, and no schema can enumerate them.

Narrow a query with the **slug**, not the title. The whole vocabulary comes back in one response unless `?limit` names a bound; `total` reports the whole of it either way, so a bounded answer says what it left out.

This resource answers `HEAD` as well, with the same headers and no body.

It also answers `OPTIONS`, which returns `Allow`, a `Link` to this description, and — where the resource takes one — the query formats it reads, as `Accept-Query`.

```json
{
  "openapi": "3.1.1",
  "info": {
    "title": "lateano.com",
    "version": "1.0.0"
  },
  "paths": {
    "/api/tags": {
      "get": {
        "operationId": "tags.get",
        "summary": "Every tag in use",
        "tags": [
          "Reading"
        ],
        "description": "The site’s tag vocabulary — each tag’s slug, the title a reader reads, and how many documents carry it in each collection. A tag nothing carries is absent.\n\nThis is the list the grammar cannot stand in for. The collections are a closed set published in [`/api/query-schema.json`](/api/query-schema.json), so a caller reading the grammar already knows them; tags are content, and no schema can enumerate them.\n\nNarrow a query with the **slug**, not the title. The whole vocabulary comes back in one response unless `?limit` names a bound; `total` reports the whole of it either way, so a bounded answer says what it left out.\n\nThis resource answers `HEAD` as well, with the same headers and no body.\n\nIt also answers `OPTIONS`, which returns `Allow`, a `Link` to this description, and — where the resource takes one — the query formats it reads, as `Accept-Query`.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "How many items to return. Omitted, the whole list comes back. A value that is not a whole number of at least 1 reads as no bound rather than as an empty page. `total` in the body says how many exist either way."
          }
        ],
        "responses": {
          "200": {
            "description": "Every tag in use",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TagsBody"
                }
              }
            }
          },
          "304": {
            "description": "The representation this request already holds is still current, so it is not sent again. No body. Read the validator off `ETag` and send it back as `If-None-Match`.",
            "headers": {
              "etag": {
                "schema": {
                  "type": "string"
                }
              },
              "cache-control": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "default": {
            "description": "Every other status this operation answers with is a refusal, and carries `problem+json` (RFC 9457).\n\nEvery refusal this API can make, whatever the status. Match on the `type` URI’s last segment — the slug below — rather than on the whole URI, which follows whichever deployment answered.\n\n| Status | Slug | Title |\n| --- | --- | --- |\n| 400 | [`invalid-idempotency-key`](https://lateano.com/api/problems/invalid-idempotency-key) | The Idempotency-Key header carries a value this resource will not accept |\n| 400 | [`malformed-body`](https://lateano.com/api/problems/malformed-body) | The request body did not parse |\n| 400 | [`missing-idempotency-key`](https://lateano.com/api/problems/missing-idempotency-key) | This resource requires an Idempotency-Key header |\n| 400 | [`missing-media-type`](https://lateano.com/api/problems/missing-media-type) | The request carried a body with no media type |\n| 404 | [`no-such-resource`](https://lateano.com/api/problems/no-such-resource) | There is nothing at this address |\n| 405 | [`method-not-allowed`](https://lateano.com/api/problems/method-not-allowed) | This resource does not answer that method |\n| 406 | [`not-acceptable`](https://lateano.com/api/problems/not-acceptable) | This resource cannot answer in any media type the request accepts |\n| 409 | [`conflicting-idempotency-key`](https://lateano.com/api/problems/conflicting-idempotency-key) | That Idempotency-Key already names a different submission |\n| 412 | [`precondition-failed`](https://lateano.com/api/problems/precondition-failed) | The condition the request carried does not hold |\n| 415 | [`unsupported-media-type`](https://lateano.com/api/problems/unsupported-media-type) | The request body is in a media type this resource does not read |\n| 422 | [`invalid-query`](https://lateano.com/api/problems/invalid-query) | The query parsed but does not describe a question this resource can answer |\n| 422 | [`invalid-submission`](https://lateano.com/api/problems/invalid-submission) | The submission parsed but is not something this resource can record |\n| 429 | [`too-many-requests`](https://lateano.com/api/problems/too-many-requests) | The courtesy quota for this caller is spent |\n| 500 | [`unexpected-failure`](https://lateano.com/api/problems/unexpected-failure) | The resource failed while answering |",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDocument"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "TagsBody": {
        "$ref": "#/components/schemas/ListEnvelope_TagInfo",
        "description": "`count` equals `total` unless a caller named a bound, and `total` is the whole vocabulary either way."
      },
      "ProblemDocument": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "§ 3.1.1. Absolute, on the origin that answered, and it resolves."
          },
          "title": {
            "type": "string",
            "description": "§ 3.1.3. The catalogue row's, unchanged from occurrence to occurrence."
          },
          "status": {
            "type": "number",
            "description": "§ 3.1.2. The same number as the status line, derived rather than passed."
          },
          "instance": {
            "type": "string",
            "description": "§ 3.1.5 RECOMMENDS an absolute URI here. This field carries a full path from the root, which is the fallback form the same section permits."
          },
          "detail": {
            "type": "string",
            "description": "§ 3.1.4. This occurrence only, bounded by `DETAIL_MAX`, absent when there is nothing to add."
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "instance"
        ],
        "additionalProperties": false,
        "description": "The schema generator publishes this declaration as the error schema, so a field here reaches the reference. RFC 9457 § 3.1 makes all five members optional, and this API narrows four of them to required: § 3.1.1 reads an absent `type` as `about:blank` and discards the resolvable URI. An optional member is absent, never present and `undefined`, because `JSON.stringify` drops the value and the wire cannot tell the two apart."
      },
      "ListEnvelope_TagInfo": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TagInfo"
            }
          },
          "count": {
            "type": "number",
            "description": "How many items this response carries."
          },
          "total": {
            "type": "number",
            "description": "How many items exist across every response."
          }
        },
        "required": [
          "items",
          "count",
          "total"
        ],
        "additionalProperties": false,
        "description": "The one body shape every list resource returns."
      },
      "TagInfo": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "count": {
            "type": "number",
            "description": "How many documents carry it, across every collection."
          },
          "byCollection": {
            "type": "object",
            "properties": {
              "writing": {
                "type": "number"
              },
              "glossary": {
                "type": "number"
              },
              "demos": {
                "type": "number"
              }
            },
            "required": [
              "writing",
              "glossary",
              "demos"
            ],
            "additionalProperties": false,
            "description": "Each entry is the answer `{ tag, collection }` returns. Total over `Collection` with zeros, so a reader never tells an absent key from a zero."
          }
        },
        "required": [
          "slug",
          "title",
          "count",
          "byCollection"
        ],
        "additionalProperties": false,
        "description": "One row of `listTags` — a tag in use."
      }
    }
  }
}
```