{
  "data": {
    "id": "KjH6d8hO6tw3OBI8K",
    "actId": "A4QB3ojaS4j3hOMXS",
    "userId": "IYgBDiXtHOijAaUel",
    "startedAt": "2026-11-09T06:07:07.073Z",
    "finishedAt": "2026-11-09T06:07:10.412Z",
    "status": "SUCCEEDED",
    "meta": {
      "origin": "CLI"
    },
    "stats": {
      "durationMillis": 3233,
      "runTimeSecs": 3.233,
      "computeUnits": 0.0035922222222222223,
      "imageSizeBytes": 319083520
    },
    "options": {
      "useCache": true,
      "betaPackages": false,
      "memoryMbytes": 4096,
      "diskMbytes": 8192
    },
    "createdByOrganizationMemberUserId": "QYo53EZkJk0prePWY",
    "exitCode": 0,
    "inputSchema": "{\n  \"title\": \"Google Fonts Scraper Input\",\n  \"description\": \"Look up Google Fonts families by name, browse by category, list the entire library, or list Material icon metadata. Search, filter, sort, and optionally enrich records with license/attribution and self-hosting file URLs.\",\n  \"type\": \"object\",\n  \"schemaVersion\": 1,\n  \"properties\": {\n    \"mode\": {\n      \"title\": \"Mode\",\n      \"type\": \"string\",\n      \"description\": \"What to fetch from Google Fonts. `icons` fetches the separate Material icon metadata document once and emits icon records; it does not return font-family records.\",\n      \"editor\": \"select\",\n      \"sectionCaption\": \"What to fetch\",\n      \"enum\": [\n        \"byFamily\",\n        \"byCategory\",\n        \"listAll\",\n        \"icons\"\n      ],\n      \"enumTitles\": [\n        \"Lookup specific font families by name\",\n        \"Browse a font category\",\n        \"List the entire library\",\n        \"List Material icon metadata (one request)\"\n      ],\n      \"default\": \"byFamily\",\n      \"prefill\": \"byFamily\"\n    },\n    \"families\": {\n      \"title\": \"Font families (mode=byFamily)\",\n      \"type\": \"array\",\n      \"description\": \"Font family names, e.g. `Roboto`, `Open Sans`, `Lora`, `Playfair Display`. Case-insensitive; `displayName` aliases and duplicates are handled. Defaults to `Roboto` when empty.\",\n      \"editor\": \"stringList\",\n      \"default\": [\n        \"Roboto\"\n      ],\n      \"prefill\": [\n        \"Roboto\"\n      ]\n    },\n    \"category\": {\n      \"title\": \"Category (mode=byCategory)\",\n      \"type\": \"string\",\n      \"description\": \"Filter the library to a single font category. Defaults to `Sans Serif` when empty. An unknown category emits a typed `invalid_category` error record.\",\n      \"editor\": \"select\",\n      \"enum\": [\n        \"Sans Serif\",\n        \"Serif\",\n        \"Display\",\n        \"Handwriting\",\n        \"Monospace\"\n      ],\n      \"enumTitles\": [\n        \"Sans Serif\",\n        \"Serif\",\n        \"Display\",\n        \"Handwriting\",\n        \"Monospace\"\n      ],\n      \"default\": \"Sans Serif\",\n      \"prefill\": \"Sans Serif\"\n    },\n    \"maxItems\": {\n      \"title\": \"Max items\",\n      \"type\": \"integer\",\n      \"description\": \"Hard cap on records emitted per run (1-5000). Defaults to 5000 so the whole font catalog (~1,959 families) fits without truncation. The icon endpoint currently reports about 6,139 icons, so `icons` mode can truncate at the cap.\",\n      \"minimum\": 1,\n      \"maximum\": 5000,\n      \"default\": 5000,\n      \"prefill\": 3\n    },\n    \"search\": {\n      \"title\": \"Search text\",\n      \"type\": \"string\",\n      \"description\": \"Free-text search over family, display name, category, classifications, subsets, designers, and primary script/language. In `icons` mode it searches icon name, categories, tags, and supported families. All space-separated tokens must match.\",\n      \"editor\": \"textfield\",\n      \"sectionCaption\": \"Search & filters\",\n      \"default\": \"\",\n      \"prefill\": \"\"\n    },\n    \"subsets\": {\n      \"title\": \"Language subsets\",\n      \"type\": \"array\",\n      \"description\": \"Only keep families that include every listed subset, e.g. `latin`, `cyrillic`, `greek`, `vietnamese`. The synthetic `menu` subset is present on every family.\",\n      \"editor\": \"stringList\",\n      \"default\": [],\n      \"prefill\": []\n    },\n    \"designers\": {\n      \"title\": \"Designers\",\n      \"type\": \"array\",\n      \"description\": \"Only keep families whose designer/foundry list contains every listed value (case-insensitive substring match).\",\n      \"editor\": \"stringList\",\n      \"default\": [],\n      \"prefill\": []\n    },\n    \"variableOnly\": {\n      \"title\": \"Variable fonts only\",\n      \"type\": \"boolean\",\n      \"description\": \"Keep only families that ship a variable font with one or more axes.\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"colorOnly\": {\n      \"title\": \"Color fonts only\",\n      \"type\": \"boolean\",\n      \"description\": \"Keep only families with color font capabilities (COLRV0/COLRV1/OTSVG).\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"brandOnly\": {\n      \"title\": \"Google-branded flag only\",\n      \"type\": \"boolean\",\n      \"description\": \"Keep only families where the upstream `isBrandFont` flag is true. In the current catalog this flag equals `isNoto`; it is not a generic brand filter.\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"notoOnly\": {\n      \"title\": \"Noto families only\",\n      \"type\": \"boolean\",\n      \"description\": \"Keep only families in the Noto collection.\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"sortBy\": {\n      \"title\": \"Sort by\",\n      \"type\": \"string\",\n      \"description\": \"Order the matched families. `popularity` and `trending` are sparse upstream snapshot ranks where lower is better (not dense 1..N positions); `dateAdded` and `lastModified` put newest first. In `icons` mode only `popularity` and `name` change the order; other values keep upstream order.\",\n      \"editor\": \"select\",\n      \"sectionCaption\": \"Sorting\",\n      \"enum\": [\n        \"default\",\n        \"popularity\",\n        \"trending\",\n        \"dateAdded\",\n        \"lastModified\",\n        \"name\",\n        \"defaultSort\"\n      ],\n      \"enumTitles\": [\n        \"Catalog order (default)\",\n        \"Popularity rank (lower = more popular)\",\n        \"Trending rank (lower = more trending)\",\n        \"Newest added first\",\n        \"Recently modified first\",\n        \"Alphabetical\",\n        \"Undocumented blended catalog order\"\n      ],\n      \"default\": \"default\",\n      \"prefill\": \"default\"\n    },\n    \"includeLicense\": {\n      \"title\": \"Include license & attribution\",\n      \"type\": \"boolean\",\n      \"description\": \"Enrich each family with license, license URL, copyright, source repository, source date, languages, unicode coverage, and description. Uses the per-family metadata endpoint, then METADATA.pb, then the download manifest. Off by default; enable it to add these fields. Raw API calls that omit the key retain the legacy `byFamily` auto-enable. The catalog-level `languages` array is always empty upstream, so `languages` can only come from this per-family enrichment. Ignored in `icons` mode.\",\n      \"sectionCaption\": \"License & self-hosting enrichment\",\n      \"prefill\": false,\n      \"default\": false\n    },\n    \"includeFiles\": {\n      \"title\": \"Include font file URLs\",\n      \"type\": \"boolean\",\n      \"description\": \"Fetch the full CSS2 stylesheet (woff2/unicode-range) and the download manifest (TTF URLs). Capped by `maxFilesPerFamily`. CSS2 output depends on the user agent: the actor requests a modern Chrome UA and retries once with Safari when Chrome receives only TTF. Large CJK families can exceed the file cap. Off by default; enable it to add these fields. Raw API calls that omit the key retain the legacy `byFamily` auto-enable. Ignored in `icons` mode.\",\n      \"prefill\": false,\n      \"default\": false\n    },\n    \"maxFilesPerFamily\": {\n      \"title\": \"Max files per family\",\n      \"type\": \"integer\",\n      \"description\": \"Cap applied separately to `fontFiles` (CSS2) and `ttfFiles` (manifest). CJK families can expose hundreds of files; for example M PLUS 1p has 882 CSS2 blocks, so the 200 default truncates it and `filesTruncated` is set. Raise to 1000 to keep them all.\",\n      \"minimum\": 1,\n      \"maximum\": 1000,\n      \"default\": 200,\n      \"prefill\": 10\n    }\n  },\n  \"required\": [\n    \"mode\"\n  ]\n}",
    "readme": "# Google Fonts Library Metadata Extractor\r\n\r\nGet structured metadata for Google's free, open-source font library instead of reading it off the fonts.google.com catalog by hand. Look a family up by name, browse a category, search/filter/sort the whole library, and optionally pull license/attribution plus self-hosting file URLs (woff2, unicode ranges, TTF).\r\n\r\nNo login, API key, cookie, or proxy configuration is required.\r\n\r\n## What this actor does\r\n\r\n- **Four modes:** `byFamily`, `byCategory`, `listAll`, `icons`\r\n- **Search and filters:** free text, language subsets, designers, variable-only, color-only, brand/Noto flags\r\n- **Sorting:** catalog order, popularity, trending, date added, last modified, alphabetical (`icons`: popularity or name)\r\n- **Every family:** weights, style variants, variable-font axes, subsets, designers, popularity/trending, dates\r\n- **Icons:** Material Icons/Symbols names, codepoints, versions, categories, tags, sizes, supported/unsupported families from one request\r\n- **Optional license pack:** license id/name/URL, copyright, source repository/commit, source date, languages, unicode coverage, description\r\n- **Optional self-hosting pack:** full CSS2 URL, woff2 files with unicode ranges, and TTF download URLs\r\n- **Safe defaults:** missing `families` or `category` fall back to `Roboto` / `Sans Serif`; empty runs are prevented\r\n- **Typed errors:** `missing_input`, `not_found`, `invalid_category`, `fetch_failed`, `css_failed`\r\n- **Empty fields are omitted:** records only contain data that actually exists.\r\n\r\n## Output per font family\r\n\r\n| Field | Description |\r\n|---|---|\r\n| `family` | Canonical family name (e.g. `Roboto`, `Open Sans`) |\r\n| `displayName` | Display alias published by Google Fonts, when present |\r\n| `category` | `Sans Serif` / `Serif` / `Display` / `Handwriting` / `Monospace` |\r\n| `stroke` | Stroke classification when present |\r\n| `classifications` | Secondary tags (e.g. `Display`, `Monospace`, `Symbols`) |\r\n| `subsets` | All subsets, including the synthetic `menu` subset |\r\n| `languageSubsets` | `subsets` with the synthetic `menu` entry removed |\r\n\r\nCommon `subsets` values include `latin`, `latin-ext`, `cyrillic`, `cyrillic-ext`, `greek`, `greek-ext`, `vietnamese`, `math`, `symbols` and script-specific CJK buckets.\r\n| `hasMenuSubset` | `true` when the family has the synthetic `menu` subset |\r\n| `weights` | Distinct numeric weights (e.g. `[\"100\",\"400\",\"700\",\"900\"]`) |\r\n| `variants` | Every shipped style, including italics (e.g. `400`, `400i`) |\r\n| `axes` | Variable-font axes: `[{tag, min, max, defaultValue}]` |\r\n| `axisDetails` | Axis display names/descriptions joined from the catalog registry |\r\n| `isVariable` | `true` when the family ships a variable font |\r\n| `designers` | Designer / foundry names |\r\n| `popularity` | Sparse popularity rank (lower = more popular) |\r\n| `trending` | Sparse growth/trend rank (lower = more trending) |\r\n| `defaultSort` | Undocumented dense catalog order (1..N) |\r\n| `dateAdded` | Catalog date the family was added/re-introduced |\r\n| `lastModified` | Catalog date the family was last updated |\r\n| `primaryScript` / `primaryLanguage` | Primary script / language when set |\r\n| `colorCapabilities` | Color-font tech when present (e.g. `COLRV1`, `OTSVG`) |\r\n| `isOpenSource` | `true` when Google Fonts marks the family open source |\r\n| `isBrandFont` | Upstream flag; in the current catalog it equals `isNoto` (not a generic brand filter) |\r\n| `isNoto` | `true` for the Noto collection |\r\n| `googleFontsUrl` | Specimen page, e.g. `https://fonts.google.com/specimen/Open+Sans` |\r\n| `cssUrl` | Backward-compatible simple CSS2 URL (weight 400 only) |\r\n| `css2UrlFull` | Full CSS2 URL covering every weight/style/variable axis |\r\n| `recordType` | `font` |\r\n| `scrapedAt` | ISO-8601 timestamp of the scrape |\r\n\r\n## Output per icon (`mode = \"icons\"`)\r\n\r\n`icons` reads `https://fonts.google.com/metadata/icons?incomplete=true` exactly once; there are no per-icon requests and no font-family records in this mode. The endpoint currently reports about 6,139 icons across 9 Material Icons/Symbols families.\r\n\r\n| Field | Description |\r\n|---|---|\r\n| `name` | Icon name (e.g. `10k`) |\r\n| `codepoint` | Decimal codepoint |\r\n| `codepointHex` | Unicode notation (e.g. `U+E951`) |\r\n| `version` | Upstream icon version |\r\n| `popularity` | Sparse icon popularity rank (lower = more popular) |\r\n| `categories` | Upstream categories |\r\n| `tags` | Search tags |\r\n| `sizesPx` | Available pixel sizes |\r\n| `unsupportedFamilies` | Material families that do not include this icon |\r\n| `supportedFamilies` | Material families that include this icon |\r\n| `recordType` | `icon` |\r\n| `scrapedAt` | ISO-8601 timestamp of the scrape |\r\n\r\n`maxItems` also applies to icons (default and maximum 5,000), so a full icon run returns at most 5,000 records. Use `search` and `sortBy` to target the rest.\r\n\r\n## Optional license & attribution fields (`includeLicense`)\r\n\r\nLicense enrichment uses three cookieless sources in order: the per-family metadata endpoint, `METADATA.pb` in the `google/fonts` repository, then the download manifest. The main catalog's `languages` array is always empty upstream, so `languages` is populated only by the per-family enrichment path.\r\n\r\n| Field | Description |\r\n|---|---|\r\n| `license` | `ofl`, `apache2`, or `ufl` |\r\n| `licenseName` | Human-readable license name |\r\n| `licenseUrl` | Official license URL |\r\n| `licenseSource` | Where the license came from: `per_family`, `metadata_pb`, or `download_list` |\r\n| `copyright` | Unique copyright line(s) |\r\n| `sourceRepositoryUrl` | Upstream source repository from `METADATA.pb` |\r\n| `sourceCommit` | Source repository commit when published |\r\n| `sourceDateAdded` | Source-repository date; distinct from catalog `dateAdded` |\r\n| `languages` | Language codes from the per-family endpoint |\r\n| `languageCount` | Number of language codes |\r\n| `unicodeCoverage` | Subset -> decimal codepoint ranges |\r\n| `description` | Family description as plain text |\r\n| `minisiteUrl` | Family minisite when published |\r\n\r\n## Optional self-hosting fields (`includeFiles`)\r\n\r\n| Field | Description |\r\n|---|---|\r\n| `fontFiles` | `[{url, format, style, weight, stretch, unicodeRange, subset}]` parsed from CSS2 |\r\n| `fileCount` | Total CSS2 `@font-face` files before capping |\r\n| `filesTruncated` | `true` when `fileCount` exceeded `maxFilesPerFamily` |\r\n| `ttfFiles` | `[{url, filename, format: \"ttf\"}]` from the download manifest |\r\n| `ttfFileCount` | Total TTF files before capping |\r\n| `ttfFilesTruncated` | `true` when the TTF list exceeded `maxFilesPerFamily` |\r\n\r\nCSS2 is user-agent dependent. The actor requests woff2 with a modern Chrome UA and retries once with a Safari UA when Chrome is served TTF only (for example Chiron Sung HK). Legacy user agents would receive TTF/WOFF without unicode ranges, so the actor does not expose a user-agent input. HTTP errors are surfaced as `css_failed` error records instead of crashing the run.\r\n\r\nCJK families can be large: M PLUS 1p emits 882 CSS2 blocks, Noto Sans SC 101, Noto Sans JP 124, Noto Sans HK 109, and Chiron Sung HK 236. `maxFilesPerFamily` defaults to 200 and is capped at 1,000; when a family exceeds the cap, `filesTruncated` / `ttfFilesTruncated` is `true` and `fileCount` / `ttfFileCount` still report the uncapped total.\r\n\r\n## Output per error (`recordType = \"error\"`)\r\n\r\n| Field | Description |\r\n|---|---|\r\n| `error` | `missing_input`, `not_found`, `invalid_category`, `fetch_failed`, or `css_failed` |\r\n| `message` | Human-readable explanation |\r\n| `input` | The input value that caused the error |\r\n| `sourceUrl` | Endpoint involved |\r\n| `scrapedAt` | ISO-8601 timestamp |\r\n\r\n## Input\r\n\r\n| Field | Type | Default | Description |\r\n|---|---|---|---|\r\n| `mode` | string | `byFamily` | `byFamily` / `byCategory` / `listAll` / `icons` |\r\n| `families` | array | `[\"Roboto\"]` | Family names (mode=byFamily), case-insensitive; `displayName` aliases work |\r\n| `category` | string | `Sans Serif` | One category (mode=byCategory) |\r\n| `maxItems` | integer | `5000` | Hard cap on records (1–5000); the font catalog fits, the ~6,139-icon endpoint can truncate |\r\n| `search` | string | empty | Free-text search over names, designers, subsets, classifications, category; in `icons` mode over name/categories/tags/families |\r\n| `subsets` | array | `[]` | Keep families that include every listed subset |\r\n| `designers` | array | `[]` | Keep families matching every listed designer/foundry |\r\n| `variableOnly` | boolean | `false` | Keep only variable fonts |\r\n| `colorOnly` | boolean | `false` | Keep only color fonts |\r\n| `brandOnly` | boolean | `false` | Keep only families with the upstream `isBrandFont` flag (currently equals `isNoto`) |\r\n| `notoOnly` | boolean | `false` | Keep only Noto families |\r\n| `sortBy` | string | `default` | `popularity` / `trending` / `dateAdded` / `lastModified` / `name` / `defaultSort`; `icons` mode uses `popularity` or `name` |\r\n| `includeLicense` | boolean | `false` | Enable license, attribution, language and coverage fields |\r\n| `includeFiles` | boolean | `false` | Enable CSS2/woff2 and TTF file URLs |\r\n| `maxFilesPerFamily` | integer | `200` | Cap applied separately to CSS2 and TTF files (1–1000) |\r\n\r\n### Example: look up specific families\r\n\r\n```json\r\n{\r\n  \"mode\": \"byFamily\",\r\n  \"families\": [\"Roboto\", \"Open Sans\", \"UNAL Ancizar Sans\", \"Playfair Display\"]\r\n}\r\n```\r\n\r\n`UNAL Ancizar Sans` resolves through its `displayName` alias to the canonical `Ancizar Sans` family.\r\n\r\n### Example: search variable color fonts\r\n\r\n```json\r\n{\r\n  \"mode\": \"listAll\",\r\n  \"search\": \"sans\",\r\n  \"variableOnly\": true,\r\n  \"colorOnly\": true,\r\n  \"sortBy\": \"popularity\",\r\n  \"maxItems\": 100\r\n}\r\n```\r\n\r\n### Example: browse every monospace font\r\n\r\n```json\r\n{\r\n  \"mode\": \"byCategory\",\r\n  \"category\": \"Monospace\",\r\n  \"maxItems\": 500\r\n}\r\n```\r\n\r\n### Example: list Material icon metadata\r\n\r\n```json\r\n{\r\n  \"mode\": \"icons\",\r\n  \"search\": \"home\",\r\n  \"sortBy\": \"popularity\",\r\n  \"maxItems\": 100\r\n}\r\n```\r\n\r\n`icons` makes one request to `https://fonts.google.com/metadata/icons?incomplete=true` and emits icon records only.\r\n\r\n### Example: export the whole library with license and files\r\n\r\n```json\r\n{\r\n  \"mode\": \"listAll\",\r\n  \"maxItems\": 5000,\r\n  \"includeLicense\": true,\r\n  \"includeFiles\": true,\r\n  \"maxFilesPerFamily\": 100\r\n}\r\n```\r\n\r\nFor a full-library run, license/file enrichment is off unless explicitly enabled: one request per family would add thousands of requests. Use `byFamily` for the richest per-family packs.\r\n\r\n## Use cases\r\n\r\n- **Design systems** — build a searchable, filterable font picker from live Google Fonts data\r\n- **Self-hosting / GDPR reviews** — pull license, copyright, source repo, woff2/unicode-range, and TTF URLs\r\n- **Web performance** — request only the weights/styles and file URLs you need before self-hosting\r\n- **Typography research** — analyse the library by category, popularity, trend, variable/color support, or date\r\n- **Icon catalogs** — export Material icon names, codepoints, categories, tags, sizes, and family support\r\n- **Localization** — filter families by language subset (Cyrillic, Greek, Vietnamese, and more)\r\n\r\n## FAQ\r\n\r\n**Where does the data come from?** The public Google Fonts metadata catalog that powers [fonts.google.com](https://fonts.google.com), plus the public per-family metadata endpoint, the `google/fonts` repository, the public download manifest, and (for `icons`) the public icon metadata endpoint. No login or API key is required.\r\n\r\n**How many fonts are covered?** The entire library — about 1,959 families today. `listAll` returns all of them up to `maxItems` (default 5,000; maximum 5,000). `icons` mode covers a separate Material icon catalog of about 6,139 icons, also capped by `maxItems`.\r\n\r\n**What are the categories?** `Sans Serif`, `Serif`, `Display`, `Handwriting`, and `Monospace`.\r\n\r\n**What's the difference between `weights` and `variants`?** `weights` lists distinct numeric weights (e.g. `400`, `700`). `variants` lists every shipped style, including italics (e.g. `400`, `400i`, `700`, `700i`).\r\n\r\n**What are `axes` and `axisDetails`?** `axes` lists the variable-font controls (`wght`, `wdth`, `slnt`, `opsz`, and custom tags) with `min`, `max`, and `defaultValue`. `axisDetails` adds the catalog's display name and description for those axis tags.\r\n\r\n**What's the difference between `cssUrl` and `css2UrlFull`?** `cssUrl` is the legacy family-only URL, which serves only weight 400. `css2UrlFull` encodes every shipped weight/style and variable axis range, e.g. `family=Roboto:ital,wdth,wght@0,75..100,100..900;1,75..100,100..900&display=swap`.\r\n\r\n**Does `includeFiles` return both woff2 and TTF?** Yes. `fontFiles` comes from CSS2 and contains woff2 URLs with unicode ranges. `ttfFiles` comes from the download manifest. Both are capped by `maxFilesPerFamily`.\r\n\r\n**Why is `menu` in `subsets`?** It is a synthetic subset Google adds to every family; `languageSubsets` removes it. About 110 menu-only families have no unicode-range/coverage data.\r\n\r\n**Is `isBrandFont` a generic brand filter?** No. In the current catalog it is true only for the Noto families and equals `isNoto`. Google Sans and TikTok Sans are `false`.\r\n\r\n**Are these fonts free to use?** Yes. The catalog currently contains about 1,919 SIL Open Font License families, 35 Apache-2.0 families, and 5 Ubuntu Font Licence families. Use `includeLicense` for exact per-family data.\r\n\r\n**What does the license enrichment cost?** One to three cookieless requests per family. It is off by default; enable `includeLicense` and/or `includeFiles` when you need those fields. Raw API calls that omit both keys keep the legacy `byFamily` auto-enable for backward compatibility.\r\n\r\n**Why is `languages` empty or missing?** The main catalog's `languages` array is empty for all 1,959 families upstream. The actor therefore does not publish it from the catalog. `includeLicense` enrichment fetches the per-family endpoint (one request per family) and writes `languages` there; even then, a few families (Noto Color Emoji, Noto Emoji, Allkin) have no language list.\r\n\r\n**Why are `popularity` and `trending` not 1..N?** They are sparse snapshot ranks from an opaque upstream universe, not catalog positions. In the current snapshot `popularity` has gaps and values above 2,000, and `trending` has hundreds of gaps and values above 2,400; both change day to day. Use `defaultSort` or `name` when you need deterministic ordering, and treat the ranks as relative signals only.\r\n\r\n**Why do some file lists stop at 200?** `maxFilesPerFamily` defaults to 200. Large CJK families emit hundreds of CSS2 blocks (M PLUS 1p: 882), so the list is capped and `filesTruncated` / `ttfFilesTruncated` is set while `fileCount` / `ttfFileCount` report the full total. Raise the cap to 1,000 to keep more. CSS2 output is also user-agent dependent; the actor uses Chrome with a Safari retry so woff2 is returned where possible.\r\n\r\n**How does `icons` mode work?** It fetches `https://fonts.google.com/metadata/icons?incomplete=true` once and emits one record per icon with codepoint, version, popularity, categories, tags, sizes, and supported/unsupported Material families. It does not fetch font-family metadata and does not make per-icon requests. Because the endpoint has about 6,139 icons and `maxItems` is capped at 5,000, use `search`/`sortBy` to target subsets.\r\n\r\n**How fresh is the data?** Live — the actor fetches the current metadata document on every run, so new families and updates appear as soon as Google publishes them.\r\n\r\n**What are the known upstream limits?** The metadata endpoints (including `metadata/icons`) are undocumented and have no SLA; `popularity`/`trending` are sparse ranks with gaps; main metadata `languages` is always empty (the per-family endpoint is used instead); `isBrandFont` exactly equals `isNoto` in the current catalog and is not a generic brand flag; `dateAdded` is the catalog date while `sourceDateAdded` is the source-repository date; CSS2 output is user-agent and CJK-payload dependent; and the upstream `size` field is not a downloadable file size, so it is not published.\r\n\r\n**Is this affiliated with Google?** No. This is an independent, third-party actor that reads Google Fonts' publicly available metadata and is not affiliated with or endorsed by Google.\r\n",
    "actorDefinition": {
      "actorSpecification": 1,
      "name": "google-fonts-library-metadata-extractor",
      "title": "Google Fonts Library Metadata Extractor",
      "description": "Search, filter, and export the Google Fonts library: categories, weights, variable axes, subsets, designers, sparse popularity/trending, license/attribution, self-hosting woff2/TTF URLs with unicode ranges, and Material icon metadata.",
      "version": "1.2",
      "buildTag": "latest",
      "dockerfile": "./Dockerfile",
      "readme": "# Google Fonts Library Metadata Extractor\r\n\r\nGet structured metadata for Google's free, open-source font library instead of reading it off the fonts.google.com catalog by hand. Look a family up by name, browse a category, search/filter/sort the whole library, and optionally pull license/attribution plus self-hosting file URLs (woff2, unicode ranges, TTF).\r\n\r\nNo login, API key, cookie, or proxy configuration is required.\r\n\r\n## What this actor does\r\n\r\n- **Four modes:** `byFamily`, `byCategory`, `listAll`, `icons`\r\n- **Search and filters:** free text, language subsets, designers, variable-only, color-only, brand/Noto flags\r\n- **Sorting:** catalog order, popularity, trending, date added, last modified, alphabetical (`icons`: popularity or name)\r\n- **Every family:** weights, style variants, variable-font axes, subsets, designers, popularity/trending, dates\r\n- **Icons:** Material Icons/Symbols names, codepoints, versions, categories, tags, sizes, supported/unsupported families from one request\r\n- **Optional license pack:** license id/name/URL, copyright, source repository/commit, source date, languages, unicode coverage, description\r\n- **Optional self-hosting pack:** full CSS2 URL, woff2 files with unicode ranges, and TTF download URLs\r\n- **Safe defaults:** missing `families` or `category` fall back to `Roboto` / `Sans Serif`; empty runs are prevented\r\n- **Typed errors:** `missing_input`, `not_found`, `invalid_category`, `fetch_failed`, `css_failed`\r\n- **Empty fields are omitted:** records only contain data that actually exists.\r\n\r\n## Output per font family\r\n\r\n| Field | Description |\r\n|---|---|\r\n| `family` | Canonical family name (e.g. `Roboto`, `Open Sans`) |\r\n| `displayName` | Display alias published by Google Fonts, when present |\r\n| `category` | `Sans Serif` / `Serif` / `Display` / `Handwriting` / `Monospace` |\r\n| `stroke` | Stroke classification when present |\r\n| `classifications` | Secondary tags (e.g. `Display`, `Monospace`, `Symbols`) |\r\n| `subsets` | All subsets, including the synthetic `menu` subset |\r\n| `languageSubsets` | `subsets` with the synthetic `menu` entry removed |\r\n\r\nCommon `subsets` values include `latin`, `latin-ext`, `cyrillic`, `cyrillic-ext`, `greek`, `greek-ext`, `vietnamese`, `math`, `symbols` and script-specific CJK buckets.\r\n| `hasMenuSubset` | `true` when the family has the synthetic `menu` subset |\r\n| `weights` | Distinct numeric weights (e.g. `[\"100\",\"400\",\"700\",\"900\"]`) |\r\n| `variants` | Every shipped style, including italics (e.g. `400`, `400i`) |\r\n| `axes` | Variable-font axes: `[{tag, min, max, defaultValue}]` |\r\n| `axisDetails` | Axis display names/descriptions joined from the catalog registry |\r\n| `isVariable` | `true` when the family ships a variable font |\r\n| `designers` | Designer / foundry names |\r\n| `popularity` | Sparse popularity rank (lower = more popular) |\r\n| `trending` | Sparse growth/trend rank (lower = more trending) |\r\n| `defaultSort` | Undocumented dense catalog order (1..N) |\r\n| `dateAdded` | Catalog date the family was added/re-introduced |\r\n| `lastModified` | Catalog date the family was last updated |\r\n| `primaryScript` / `primaryLanguage` | Primary script / language when set |\r\n| `colorCapabilities` | Color-font tech when present (e.g. `COLRV1`, `OTSVG`) |\r\n| `isOpenSource` | `true` when Google Fonts marks the family open source |\r\n| `isBrandFont` | Upstream flag; in the current catalog it equals `isNoto` (not a generic brand filter) |\r\n| `isNoto` | `true` for the Noto collection |\r\n| `googleFontsUrl` | Specimen page, e.g. `https://fonts.google.com/specimen/Open+Sans` |\r\n| `cssUrl` | Backward-compatible simple CSS2 URL (weight 400 only) |\r\n| `css2UrlFull` | Full CSS2 URL covering every weight/style/variable axis |\r\n| `recordType` | `font` |\r\n| `scrapedAt` | ISO-8601 timestamp of the scrape |\r\n\r\n## Output per icon (`mode = \"icons\"`)\r\n\r\n`icons` reads `https://fonts.google.com/metadata/icons?incomplete=true` exactly once; there are no per-icon requests and no font-family records in this mode. The endpoint currently reports about 6,139 icons across 9 Material Icons/Symbols families.\r\n\r\n| Field | Description |\r\n|---|---|\r\n| `name` | Icon name (e.g. `10k`) |\r\n| `codepoint` | Decimal codepoint |\r\n| `codepointHex` | Unicode notation (e.g. `U+E951`) |\r\n| `version` | Upstream icon version |\r\n| `popularity` | Sparse icon popularity rank (lower = more popular) |\r\n| `categories` | Upstream categories |\r\n| `tags` | Search tags |\r\n| `sizesPx` | Available pixel sizes |\r\n| `unsupportedFamilies` | Material families that do not include this icon |\r\n| `supportedFamilies` | Material families that include this icon |\r\n| `recordType` | `icon` |\r\n| `scrapedAt` | ISO-8601 timestamp of the scrape |\r\n\r\n`maxItems` also applies to icons (default and maximum 5,000), so a full icon run returns at most 5,000 records. Use `search` and `sortBy` to target the rest.\r\n\r\n## Optional license & attribution fields (`includeLicense`)\r\n\r\nLicense enrichment uses three cookieless sources in order: the per-family metadata endpoint, `METADATA.pb` in the `google/fonts` repository, then the download manifest. The main catalog's `languages` array is always empty upstream, so `languages` is populated only by the per-family enrichment path.\r\n\r\n| Field | Description |\r\n|---|---|\r\n| `license` | `ofl`, `apache2`, or `ufl` |\r\n| `licenseName` | Human-readable license name |\r\n| `licenseUrl` | Official license URL |\r\n| `licenseSource` | Where the license came from: `per_family`, `metadata_pb`, or `download_list` |\r\n| `copyright` | Unique copyright line(s) |\r\n| `sourceRepositoryUrl` | Upstream source repository from `METADATA.pb` |\r\n| `sourceCommit` | Source repository commit when published |\r\n| `sourceDateAdded` | Source-repository date; distinct from catalog `dateAdded` |\r\n| `languages` | Language codes from the per-family endpoint |\r\n| `languageCount` | Number of language codes |\r\n| `unicodeCoverage` | Subset -> decimal codepoint ranges |\r\n| `description` | Family description as plain text |\r\n| `minisiteUrl` | Family minisite when published |\r\n\r\n## Optional self-hosting fields (`includeFiles`)\r\n\r\n| Field | Description |\r\n|---|---|\r\n| `fontFiles` | `[{url, format, style, weight, stretch, unicodeRange, subset}]` parsed from CSS2 |\r\n| `fileCount` | Total CSS2 `@font-face` files before capping |\r\n| `filesTruncated` | `true` when `fileCount` exceeded `maxFilesPerFamily` |\r\n| `ttfFiles` | `[{url, filename, format: \"ttf\"}]` from the download manifest |\r\n| `ttfFileCount` | Total TTF files before capping |\r\n| `ttfFilesTruncated` | `true` when the TTF list exceeded `maxFilesPerFamily` |\r\n\r\nCSS2 is user-agent dependent. The actor requests woff2 with a modern Chrome UA and retries once with a Safari UA when Chrome is served TTF only (for example Chiron Sung HK). Legacy user agents would receive TTF/WOFF without unicode ranges, so the actor does not expose a user-agent input. HTTP errors are surfaced as `css_failed` error records instead of crashing the run.\r\n\r\nCJK families can be large: M PLUS 1p emits 882 CSS2 blocks, Noto Sans SC 101, Noto Sans JP 124, Noto Sans HK 109, and Chiron Sung HK 236. `maxFilesPerFamily` defaults to 200 and is capped at 1,000; when a family exceeds the cap, `filesTruncated` / `ttfFilesTruncated` is `true` and `fileCount` / `ttfFileCount` still report the uncapped total.\r\n\r\n## Output per error (`recordType = \"error\"`)\r\n\r\n| Field | Description |\r\n|---|---|\r\n| `error` | `missing_input`, `not_found`, `invalid_category`, `fetch_failed`, or `css_failed` |\r\n| `message` | Human-readable explanation |\r\n| `input` | The input value that caused the error |\r\n| `sourceUrl` | Endpoint involved |\r\n| `scrapedAt` | ISO-8601 timestamp |\r\n\r\n## Input\r\n\r\n| Field | Type | Default | Description |\r\n|---|---|---|---|\r\n| `mode` | string | `byFamily` | `byFamily` / `byCategory` / `listAll` / `icons` |\r\n| `families` | array | `[\"Roboto\"]` | Family names (mode=byFamily), case-insensitive; `displayName` aliases work |\r\n| `category` | string | `Sans Serif` | One category (mode=byCategory) |\r\n| `maxItems` | integer | `5000` | Hard cap on records (1–5000); the font catalog fits, the ~6,139-icon endpoint can truncate |\r\n| `search` | string | empty | Free-text search over names, designers, subsets, classifications, category; in `icons` mode over name/categories/tags/families |\r\n| `subsets` | array | `[]` | Keep families that include every listed subset |\r\n| `designers` | array | `[]` | Keep families matching every listed designer/foundry |\r\n| `variableOnly` | boolean | `false` | Keep only variable fonts |\r\n| `colorOnly` | boolean | `false` | Keep only color fonts |\r\n| `brandOnly` | boolean | `false` | Keep only families with the upstream `isBrandFont` flag (currently equals `isNoto`) |\r\n| `notoOnly` | boolean | `false` | Keep only Noto families |\r\n| `sortBy` | string | `default` | `popularity` / `trending` / `dateAdded` / `lastModified` / `name` / `defaultSort`; `icons` mode uses `popularity` or `name` |\r\n| `includeLicense` | boolean | `false` | Enable license, attribution, language and coverage fields |\r\n| `includeFiles` | boolean | `false` | Enable CSS2/woff2 and TTF file URLs |\r\n| `maxFilesPerFamily` | integer | `200` | Cap applied separately to CSS2 and TTF files (1–1000) |\r\n\r\n### Example: look up specific families\r\n\r\n```json\r\n{\r\n  \"mode\": \"byFamily\",\r\n  \"families\": [\"Roboto\", \"Open Sans\", \"UNAL Ancizar Sans\", \"Playfair Display\"]\r\n}\r\n```\r\n\r\n`UNAL Ancizar Sans` resolves through its `displayName` alias to the canonical `Ancizar Sans` family.\r\n\r\n### Example: search variable color fonts\r\n\r\n```json\r\n{\r\n  \"mode\": \"listAll\",\r\n  \"search\": \"sans\",\r\n  \"variableOnly\": true,\r\n  \"colorOnly\": true,\r\n  \"sortBy\": \"popularity\",\r\n  \"maxItems\": 100\r\n}\r\n```\r\n\r\n### Example: browse every monospace font\r\n\r\n```json\r\n{\r\n  \"mode\": \"byCategory\",\r\n  \"category\": \"Monospace\",\r\n  \"maxItems\": 500\r\n}\r\n```\r\n\r\n### Example: list Material icon metadata\r\n\r\n```json\r\n{\r\n  \"mode\": \"icons\",\r\n  \"search\": \"home\",\r\n  \"sortBy\": \"popularity\",\r\n  \"maxItems\": 100\r\n}\r\n```\r\n\r\n`icons` makes one request to `https://fonts.google.com/metadata/icons?incomplete=true` and emits icon records only.\r\n\r\n### Example: export the whole library with license and files\r\n\r\n```json\r\n{\r\n  \"mode\": \"listAll\",\r\n  \"maxItems\": 5000,\r\n  \"includeLicense\": true,\r\n  \"includeFiles\": true,\r\n  \"maxFilesPerFamily\": 100\r\n}\r\n```\r\n\r\nFor a full-library run, license/file enrichment is off unless explicitly enabled: one request per family would add thousands of requests. Use `byFamily` for the richest per-family packs.\r\n\r\n## Use cases\r\n\r\n- **Design systems** — build a searchable, filterable font picker from live Google Fonts data\r\n- **Self-hosting / GDPR reviews** — pull license, copyright, source repo, woff2/unicode-range, and TTF URLs\r\n- **Web performance** — request only the weights/styles and file URLs you need before self-hosting\r\n- **Typography research** — analyse the library by category, popularity, trend, variable/color support, or date\r\n- **Icon catalogs** — export Material icon names, codepoints, categories, tags, sizes, and family support\r\n- **Localization** — filter families by language subset (Cyrillic, Greek, Vietnamese, and more)\r\n\r\n## FAQ\r\n\r\n**Where does the data come from?** The public Google Fonts metadata catalog that powers [fonts.google.com](https://fonts.google.com), plus the public per-family metadata endpoint, the `google/fonts` repository, the public download manifest, and (for `icons`) the public icon metadata endpoint. No login or API key is required.\r\n\r\n**How many fonts are covered?** The entire library — about 1,959 families today. `listAll` returns all of them up to `maxItems` (default 5,000; maximum 5,000). `icons` mode covers a separate Material icon catalog of about 6,139 icons, also capped by `maxItems`.\r\n\r\n**What are the categories?** `Sans Serif`, `Serif`, `Display`, `Handwriting`, and `Monospace`.\r\n\r\n**What's the difference between `weights` and `variants`?** `weights` lists distinct numeric weights (e.g. `400`, `700`). `variants` lists every shipped style, including italics (e.g. `400`, `400i`, `700`, `700i`).\r\n\r\n**What are `axes` and `axisDetails`?** `axes` lists the variable-font controls (`wght`, `wdth`, `slnt`, `opsz`, and custom tags) with `min`, `max`, and `defaultValue`. `axisDetails` adds the catalog's display name and description for those axis tags.\r\n\r\n**What's the difference between `cssUrl` and `css2UrlFull`?** `cssUrl` is the legacy family-only URL, which serves only weight 400. `css2UrlFull` encodes every shipped weight/style and variable axis range, e.g. `family=Roboto:ital,wdth,wght@0,75..100,100..900;1,75..100,100..900&display=swap`.\r\n\r\n**Does `includeFiles` return both woff2 and TTF?** Yes. `fontFiles` comes from CSS2 and contains woff2 URLs with unicode ranges. `ttfFiles` comes from the download manifest. Both are capped by `maxFilesPerFamily`.\r\n\r\n**Why is `menu` in `subsets`?** It is a synthetic subset Google adds to every family; `languageSubsets` removes it. About 110 menu-only families have no unicode-range/coverage data.\r\n\r\n**Is `isBrandFont` a generic brand filter?** No. In the current catalog it is true only for the Noto families and equals `isNoto`. Google Sans and TikTok Sans are `false`.\r\n\r\n**Are these fonts free to use?** Yes. The catalog currently contains about 1,919 SIL Open Font License families, 35 Apache-2.0 families, and 5 Ubuntu Font Licence families. Use `includeLicense` for exact per-family data.\r\n\r\n**What does the license enrichment cost?** One to three cookieless requests per family. It is off by default; enable `includeLicense` and/or `includeFiles` when you need those fields. Raw API calls that omit both keys keep the legacy `byFamily` auto-enable for backward compatibility.\r\n\r\n**Why is `languages` empty or missing?** The main catalog's `languages` array is empty for all 1,959 families upstream. The actor therefore does not publish it from the catalog. `includeLicense` enrichment fetches the per-family endpoint (one request per family) and writes `languages` there; even then, a few families (Noto Color Emoji, Noto Emoji, Allkin) have no language list.\r\n\r\n**Why are `popularity` and `trending` not 1..N?** They are sparse snapshot ranks from an opaque upstream universe, not catalog positions. In the current snapshot `popularity` has gaps and values above 2,000, and `trending` has hundreds of gaps and values above 2,400; both change day to day. Use `defaultSort` or `name` when you need deterministic ordering, and treat the ranks as relative signals only.\r\n\r\n**Why do some file lists stop at 200?** `maxFilesPerFamily` defaults to 200. Large CJK families emit hundreds of CSS2 blocks (M PLUS 1p: 882), so the list is capped and `filesTruncated` / `ttfFilesTruncated` is set while `fileCount` / `ttfFileCount` report the full total. Raise the cap to 1,000 to keep more. CSS2 output is also user-agent dependent; the actor uses Chrome with a Safari retry so woff2 is returned where possible.\r\n\r\n**How does `icons` mode work?** It fetches `https://fonts.google.com/metadata/icons?incomplete=true` once and emits one record per icon with codepoint, version, popularity, categories, tags, sizes, and supported/unsupported Material families. It does not fetch font-family metadata and does not make per-icon requests. Because the endpoint has about 6,139 icons and `maxItems` is capped at 5,000, use `search`/`sortBy` to target subsets.\r\n\r\n**How fresh is the data?** Live — the actor fetches the current metadata document on every run, so new families and updates appear as soon as Google publishes them.\r\n\r\n**What are the known upstream limits?** The metadata endpoints (including `metadata/icons`) are undocumented and have no SLA; `popularity`/`trending` are sparse ranks with gaps; main metadata `languages` is always empty (the per-family endpoint is used instead); `isBrandFont` exactly equals `isNoto` in the current catalog and is not a generic brand flag; `dateAdded` is the catalog date while `sourceDateAdded` is the source-repository date; CSS2 output is user-agent and CJK-payload dependent; and the upstream `size` field is not a downloadable file size, so it is not published.\r\n\r\n**Is this affiliated with Google?** No. This is an independent, third-party actor that reads Google Fonts' publicly available metadata and is not affiliated with or endorsed by Google.\r\n",
      "input": {
        "title": "Google Fonts Scraper Input",
        "description": "Look up Google Fonts families by name, browse by category, list the entire library, or list Material icon metadata. Search, filter, sort, and optionally enrich records with license/attribution and self-hosting file URLs.",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "mode": {
            "title": "Mode",
            "type": "string",
            "description": "What to fetch from Google Fonts. `icons` fetches the separate Material icon metadata document once and emits icon records; it does not return font-family records.",
            "editor": "select",
            "sectionCaption": "What to fetch",
            "enum": [
              "byFamily",
              "byCategory",
              "listAll",
              "icons"
            ],
            "enumTitles": [
              "Lookup specific font families by name",
              "Browse a font category",
              "List the entire library",
              "List Material icon metadata (one request)"
            ],
            "default": "byFamily",
            "prefill": "byFamily"
          },
          "families": {
            "title": "Font families (mode=byFamily)",
            "type": "array",
            "description": "Font family names, e.g. `Roboto`, `Open Sans`, `Lora`, `Playfair Display`. Case-insensitive; `displayName` aliases and duplicates are handled. Defaults to `Roboto` when empty.",
            "editor": "stringList",
            "default": [
              "Roboto"
            ],
            "prefill": [
              "Roboto"
            ]
          },
          "category": {
            "title": "Category (mode=byCategory)",
            "type": "string",
            "description": "Filter the library to a single font category. Defaults to `Sans Serif` when empty. An unknown category emits a typed `invalid_category` error record.",
            "editor": "select",
            "enum": [
              "Sans Serif",
              "Serif",
              "Display",
              "Handwriting",
              "Monospace"
            ],
            "enumTitles": [
              "Sans Serif",
              "Serif",
              "Display",
              "Handwriting",
              "Monospace"
            ],
            "default": "Sans Serif",
            "prefill": "Sans Serif"
          },
          "maxItems": {
            "title": "Max items",
            "type": "integer",
            "description": "Hard cap on records emitted per run (1-5000). Defaults to 5000 so the whole font catalog (~1,959 families) fits without truncation. The icon endpoint currently reports about 6,139 icons, so `icons` mode can truncate at the cap.",
            "minimum": 1,
            "maximum": 5000,
            "default": 5000,
            "prefill": 3
          },
          "search": {
            "title": "Search text",
            "type": "string",
            "description": "Free-text search over family, display name, category, classifications, subsets, designers, and primary script/language. In `icons` mode it searches icon name, categories, tags, and supported families. All space-separated tokens must match.",
            "editor": "textfield",
            "sectionCaption": "Search & filters",
            "default": "",
            "prefill": ""
          },
          "subsets": {
            "title": "Language subsets",
            "type": "array",
            "description": "Only keep families that include every listed subset, e.g. `latin`, `cyrillic`, `greek`, `vietnamese`. The synthetic `menu` subset is present on every family.",
            "editor": "stringList",
            "default": [],
            "prefill": []
          },
          "designers": {
            "title": "Designers",
            "type": "array",
            "description": "Only keep families whose designer/foundry list contains every listed value (case-insensitive substring match).",
            "editor": "stringList",
            "default": [],
            "prefill": []
          },
          "variableOnly": {
            "title": "Variable fonts only",
            "type": "boolean",
            "description": "Keep only families that ship a variable font with one or more axes.",
            "default": false,
            "prefill": false
          },
          "colorOnly": {
            "title": "Color fonts only",
            "type": "boolean",
            "description": "Keep only families with color font capabilities (COLRV0/COLRV1/OTSVG).",
            "default": false,
            "prefill": false
          },
          "brandOnly": {
            "title": "Google-branded flag only",
            "type": "boolean",
            "description": "Keep only families where the upstream `isBrandFont` flag is true. In the current catalog this flag equals `isNoto`; it is not a generic brand filter.",
            "default": false,
            "prefill": false
          },
          "notoOnly": {
            "title": "Noto families only",
            "type": "boolean",
            "description": "Keep only families in the Noto collection.",
            "default": false,
            "prefill": false
          },
          "sortBy": {
            "title": "Sort by",
            "type": "string",
            "description": "Order the matched families. `popularity` and `trending` are sparse upstream snapshot ranks where lower is better (not dense 1..N positions); `dateAdded` and `lastModified` put newest first. In `icons` mode only `popularity` and `name` change the order; other values keep upstream order.",
            "editor": "select",
            "sectionCaption": "Sorting",
            "enum": [
              "default",
              "popularity",
              "trending",
              "dateAdded",
              "lastModified",
              "name",
              "defaultSort"
            ],
            "enumTitles": [
              "Catalog order (default)",
              "Popularity rank (lower = more popular)",
              "Trending rank (lower = more trending)",
              "Newest added first",
              "Recently modified first",
              "Alphabetical",
              "Undocumented blended catalog order"
            ],
            "default": "default",
            "prefill": "default"
          },
          "includeLicense": {
            "title": "Include license & attribution",
            "type": "boolean",
            "description": "Enrich each family with license, license URL, copyright, source repository, source date, languages, unicode coverage, and description. Uses the per-family metadata endpoint, then METADATA.pb, then the download manifest. Off by default; enable it to add these fields. Raw API calls that omit the key retain the legacy `byFamily` auto-enable. The catalog-level `languages` array is always empty upstream, so `languages` can only come from this per-family enrichment. Ignored in `icons` mode.",
            "sectionCaption": "License & self-hosting enrichment",
            "prefill": false,
            "default": false
          },
          "includeFiles": {
            "title": "Include font file URLs",
            "type": "boolean",
            "description": "Fetch the full CSS2 stylesheet (woff2/unicode-range) and the download manifest (TTF URLs). Capped by `maxFilesPerFamily`. CSS2 output depends on the user agent: the actor requests a modern Chrome UA and retries once with Safari when Chrome receives only TTF. Large CJK families can exceed the file cap. Off by default; enable it to add these fields. Raw API calls that omit the key retain the legacy `byFamily` auto-enable. Ignored in `icons` mode.",
            "prefill": false,
            "default": false
          },
          "maxFilesPerFamily": {
            "title": "Max files per family",
            "type": "integer",
            "description": "Cap applied separately to `fontFiles` (CSS2) and `ttfFiles` (manifest). CJK families can expose hundreds of files; for example M PLUS 1p has 882 CSS2 blocks, so the 200 default truncates it and `filesTruncated` is set. Raise to 1000 to keep them all.",
            "minimum": 1,
            "maximum": 1000,
            "default": 200,
            "prefill": 10
          }
        },
        "required": [
          "mode"
        ]
      },
      "output": {
        "actorOutputSchemaVersion": 1,
        "title": "Google Fonts Library Metadata Extractor Output",
        "description": "Google Fonts family records with category, weights, variable-font axes, subsets, designers, sparse popularity/trending ranks, dates, and specimen/CSS URLs. Optional enrichment adds license/attribution, source repository, languages/unicode coverage, CSS2 woff2 files with unicode ranges, and TTF download URLs. The icons mode emits icon records (name, codepoint, version, popularity, categories, tags, sizes, supported/unsupported families) from a single request. Typed error records use recordType=error; font records use recordType=font; icon records use recordType=icon.",
        "properties": {
          "fonts": {
            "type": "string",
            "title": "Google Fonts",
            "description": "Dataset containing the scraped Google Fonts families and any typed error records.",
            "template": "{{links.apiDefaultDatasetUrl}}/items"
          }
        }
      },
      "storages": {
        "dataset": {
          "actorSpecification": 1,
          "views": {
            "overview": {
              "title": "Fonts",
              "transformation": {
                "fields": [
                  "family",
                  "category",
                  "isVariable",
                  "weights",
                  "subsets",
                  "designers",
                  "popularity",
                  "trending",
                  "dateAdded",
                  "license",
                  "googleFontsUrl",
                  "css2UrlFull",
                  "fileCount"
                ]
              },
              "display": {
                "component": "table",
                "properties": {}
              }
            },
            "icons": {
              "title": "Icons",
              "transformation": {
                "fields": [
                  "name",
                  "codepointHex",
                  "categories",
                  "tags",
                  "sizesPx",
                  "supportedFamilies",
                  "popularity",
                  "version"
                ]
              },
              "display": {
                "component": "table",
                "properties": {}
              }
            }
          }
        }
      },
      "environmentVariables": {}
    },
    "buildNumber": "1.2.3",
    "imageDigest": "7d959ee5d9e4a3fb2d793c09f141da8a6f88a52571fa5401db3105262974de60",
    "actVersion": {
      "sourceType": "SOURCE_FILES",
      "buildTag": "latest",
      "versionNumber": "1.2"
    },
    "usage": {
      "ACTOR_COMPUTE_UNITS": 0.0035922222222222223
    }
  }
}