# Extension Tracker data: files and columns

Extension Tracker keeps a public record of the extensions in the Chrome Web
Store. Every Sunday we publish a new set of data files at
`https://data.extensiontracker.com/YYYY-MM-DD/`, named for that day, and
`https://data.extensiontracker.com/latest.json` always describes the newest
set. This page explains each file and every column. A copy of it comes with
every set, as `SCHEMA.md`.

How the data is collected is explained at
<https://extensiontracker.com/methodology>.

## Reading the files

You can download the files, or read them where they are: partial downloads
work, and browsers may read them from any site. DuckDB, for example, reads only
the columns a query needs:

```sql
SELECT name, users
FROM 'https://data.extensiontracker.com/2026-09-29/extensions-2026-09-29.parquet'
ORDER BY users DESC
LIMIT 10;
```

`https://data.extensiontracker.com/latest.json` names the newest set's files,
and `https://data.extensiontracker.com/index.json` lists every set.

## License and credit

The data is free to reuse under the Creative Commons Attribution 4.0 license
(<https://creativecommons.org/licenses/by/4.0/>), including for commercial
purposes, as long as you credit Extension Tracker and link to
<https://extensiontracker.com>. Extension names, summaries and other text
written by developers belong to them, and the license doesn't cover it.

The data is collected automatically, so it can be wrong or out of date. Before
relying on an important fact, check it against the extension's Chrome Web
Store listing.

## Conventions

- **Extensions** are identified by their 32-letter store ID. An extension's
  page on our site is `https://extensiontracker.com/extension/<id>`, and its
  store listing is `https://chromewebstore.google.com/detail/<id>`.
- **Dates and times are in UTC.** Dates are written `YYYY-MM-DD`, and in the
  CSV files times are written as ISO 8601 ending in `Z`.
- **In the CSV files,** lists are JSON arrays, true and false are written out,
  and an empty field means there's no value.
- **Figures from the store,** such as users and ratings, are the store's own, as
  its listings show them.
- **Withdrawn extensions:** an extension whose page we withdrew at its
  developer's request is left out of every file. The totals in the coverage
  file still count it.

## Files

| File | What it holds |
|------|---------------|
| `extensions-YYYY-MM-DD.parquet` and `.csv.gz` | One row for every extension we track, as of that day, including removed ones |
| `history-YYYY-MM-DD.parquet` | The users and rating recorded at every check of every extension |
| `changes-YYYY-MM-DD.parquet` | Every change we recorded, one row per changed field |
| `other-items-YYYY-MM-DD.csv.gz` | One row for every other item the store lists: themes, apps, and items we couldn't view |
| `coverage-YYYY-MM-DD.json` | How many items we hold, by type and status, and the latest daily runs |
| `SCHEMA.md` | This page |
| `manifest.json` | Every file's address, size, number of rows and SHA-256 checksum |

## Extensions

One row per extension, including extensions that have left the store. Most
figures come from the extension's store listing, as we last read it
(`data_as_of`).

| Column | Type | Meaning |
|--------|------|---------|
| `id` | string | The extension's store ID |
| `name` | string | Its name on the listing |
| `status` | string | `live`; `hidden`, when the store lists the extension but won't show its page to our crawler; `missing`, when the store says it doesn't know the ID; `unlisted`, when the store's daily lists have left it out but the store still offers it for Chrome to install, so anyone with a direct link can get it; `removed`, once it has left the store's daily lists and the store no longer offers it; or `pending`, while a fresh check is due |
| `category` | string | The store's category key, such as `productivity/tools` |
| `users` | integer | Its number of users |
| `rating` | float | Its average rating, from 1 to 5; 0 or empty when it has no ratings |
| `rating_count` | integer | How many ratings it has |
| `version` | string | Its version |
| `published_at` | time | When it was first published in the store |
| `updated_at` | time | When its latest version was published |
| `size` | string | Its size as the listing shows it, such as `9.25MiB` |
| `languages` | list of strings | The languages the listing names |
| `summary` | string | The short summary on the listing, written by the developer |
| `website` | string | The developer's website, as given on the listing |
| `support_url` | string | The support page given on the listing |
| `privacy_policy_url` | string | The privacy policy given on the listing |
| `icon_url` | string | The address of its icon on Google's servers |
| `developer_name` | string | The display name of the developer's store account, which is often a person's name |
| `business_name` | string | The business name the store lists for the developer as a trader, when it comes with a D-U-N-S number; otherwise empty. Our pages show it instead of `developer_name` when there is one. Filled in from 30 September 2026, as each extension is next checked |
| `publisher_id` | string | The store's ID for the publisher account. Our developer pages group extensions by it |
| `is_trader` | boolean | Whether the listing declares the developer a trader under EU law |
| `manifest_version` | integer | The manifest version, 2 or 3 |
| `min_chrome_version` | string | The oldest Chrome version it supports |
| `permissions` | list of strings | The permissions its manifest asks for |
| `host_permissions` | list of strings | The sites its manifest asks for access to |
| `optional_permissions` | list of strings | Permissions it may ask for later |
| `optional_host_permissions` | list of strings | Site access it may ask for later |
| `content_script_matches` | list of strings | The sites its content scripts run on |
| `first_seen` | date | The first day the store's list of items included it. For extensions already listed when tracking began, that's 26 September 2026 |
| `last_seen` | date | The last day the store's list of items included it |
| `removed_on` | date | For a removed extension, the first day it was missing, or for one that was unlisted first, the day the store stopped offering it |
| `unlisted_on` | date | For an unlisted extension, the first day the store's lists left it out |
| `data_as_of` | time | When we last read its listing |
| `last_checked_at` | time | When we last tried to read its listing, whatever the result |
| `users_gained_7d` | integer | Users gained since a count taken at least 7 days earlier |
| `users_gained_30d` | integer | Users gained since a count taken at least 30 days earlier |
| `rank_overall` | integer | Its rank among live extensions by users. Extensions with the same count share a rank |
| `rank_category` | integer | Its rank among live extensions in its category |
| `pkg_status` | string | From Google's update service: `ok` when it offers a package, `noupdate` when it doesn't, as for a removed extension, and `unknown` when it doesn't know the ID |
| `pkg_version` | string | The version of the package the update service offers |
| `pkg_size` | integer | The package's size in bytes |
| `pkg_sha256` | string | The package's SHA-256 checksum |
| `esb_allowlist` | boolean | Whether Chrome's Enhanced Safe Browsing trusts the extension, from the `_esbAllowlist` flag in Google's update service's answer. Google doesn't document the flag; Chrome's code reads it to decide which extensions to warn about. See https://extensiontracker.com/enhanced-safe-browsing |
| `google_flag` | string | The flag Google's update service sets on the extension: `malware`, `policy_violation` or `potentially_unwanted`, otherwise empty. These are Google's flags, not our findings. Most removed extensions have none. Removed extensions are only asked about for 10 days after they leave |
| `pkg_checked_at` | time | When we last asked the update service |

## History

One row for every check of an extension that recorded its figures. The
history starts on 26 September 2026. Extensions with 1,000 or more users are
checked every week and the rest every month, and any extension is checked again
when a new version appears.

| Column | Type | Meaning |
|--------|------|---------|
| `id` | string | The extension's store ID |
| `date` | date | The day of the check |
| `users` | integer | Its number of users that day |
| `rating` | float | Its average rating that day |
| `rating_count` | integer | How many ratings it had that day |

## Changes

One row for each field that changed. The fields are the extensions file's
columns of the same name: `name`, `category`, `version`, `summary`, `size`,
`languages`, `website`, `privacy_policy_url`, `developer_name`, `business_name`,
`publisher_id`, `is_trader`, `min_chrome_version`, `manifest_version`,
`permissions`, `host_permissions`, `optional_permissions`,
`optional_host_permissions`, `content_script_matches`, `status`, `pkg_version`,
`esb_allowlist` and `google_flag`. A
`developer_key` row means the developer's contact email changed. The files
never carry the email or its keyed hash, so both values are empty. Our pages
say which accounts share an email, without showing it.
A `publisher_id` row is a move to another publisher account, with both IDs. A
removed extension that the store lists again gets a `status` row from `removed`
to `pending`, and the check that follows sets it back to `live` without another
row. An unlisted one listed again goes from `unlisted` to `pending` the same
way.
Items other than extensions only have `status` rows.

| Column | Type | Meaning |
|--------|------|---------|
| `id` | string | The item's store ID |
| `date` | date | The day we noticed the change |
| `field` | string | What changed |
| `old_value` | string | The value before, as JSON: a string in quotes, a number, `true` or `false`, or a list |
| `new_value` | string | The value after, in the same form |

## Other items

One row for every item the store lists that isn't an extension we track.
Themes and apps are read once, to learn what they are, and aren't checked
after that, so their figures are as of `last_checked_at`.

| Column | Type | Meaning |
|--------|------|---------|
| `id` | string | The item's store ID |
| `item_type` | string | `theme` or `app`; empty when we couldn't read the listing |
| `status` | string | As in the extensions file |
| `name` | string | Its name, when we read it |
| `category` | string | The store's category key |
| `users` | integer | Its number of users when we read it |
| `rating` | float | Its average rating when we read it |
| `rating_count` | integer | How many ratings it had when we read it |
| `first_seen` | date | The first day the store's list of items included it |
| `last_seen` | date | The last day the store's list of items included it |
| `removed_on` | date | For a removed item, the first day it was missing |
| `last_checked_at` | time | When we last tried to read its listing |

## Coverage

A JSON object:

- `items`: the number of items for each type (`extension`, `theme`, `app`, or
  `unclassified` when we couldn't read the listing) and status;
- `total`: every item we've ever seen in the store's list;
- `listed` and `listed_on`: how many items the store's latest list included,
  and the day we read it;
- `fetched`, `parsed` and `failing`: how many items we've tried to read, how
  many we've read at least once, and how many failed on their latest try;
- `withdrawn`: extensions withdrawn at their developer's request, which the
  other files leave out;
- `latest_runs`: when the latest daily runs happened, and their counts.

## Manifest

`manifest.json` names the set's day and license, and lists each file with its
address, size in bytes, number of rows and SHA-256 checksum.
