Fathom Analytics Milestones API
Milestone management per site
Milestone management per site
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/fathom:fathom-milestones-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Fathom Analytics Milestones API
version: v1
summary: Manage sites, pull reports, and track events from your own code.
description: '## Fathom Analytics API
Use the API to manage sites, events and milestones, or pull custom reports. The base URL is `https://api.usefathom.com/v1`. Each request counts towards your monthly pageviews. Ten Create Site calls count the same as ten pageviews. That keeps the API flexible for you without us having to raise plan prices to cover it. The first time anyone runs a unique aggregation, there is a short delay while the database builds a query plan. After that, it is fast. > See our community guidelines for what you can and can''t do when building plugins, integrations or connections to the API. ## Rate limits and concurrency
The API has two separate limits, both applied per account across every endpoint:
- A **per-hour** limit on all API requests. This is the sustained-volume backstop; concurrency is the primary safeguard. - A **concurrency** limit: how many requests you can have in flight at the same time, across all endpoints. Every account includes a free allowance. Need more? Upgrade your API plan in your billing settings. Plans are self-serve and apply immediately:
| Plan | Price | Per hour All endpoints | Concurrent All endpoints |
| --- | --- | --- | --- |
| Tier 1 | Included | 600 | 5 |
| Tier 2 | $19/mo | 1,000 | 5 |
| Tier 3 | $39/mo | 2,000 | 10 |
| Tier 4 | $79/mo | 4,000 | 15 |
| Tier 5 | $199/mo | 8,000 | 20 |
| Tier 6 | $399/mo | 16,000 | 25 |
When you exceed any limit we return a `429` with a `Retry-After` header. Wait that many seconds, then retry. ## Create a token
To create a token for our API, go to https://app.usefathom.com/api and click `Create new`. - Click `Create new` from the API page. - Give your token a name, and set permissions (Admin, read only (all sites), or site-specific). - Click `Save changes`. - Write down your API token and save it in a safe place. You won''t be able to retrieve your token once you navigate away from that page. ## Authentication
To connect to our API, you must use the `Authorization` header with a bearer token. This can be obtained from your dashboard, under Settings → API. Your API key has complete access to your account (depending on the permissions you set), so please protect it. You should also send `Accept: application/json` so that errors are returned as JSON. ## Pagination
All top-level API resources allow you to fetch records in bulk via our "list" methods. These list methods share a common, cursor-based pagination structure. We''ve taken inspiration from the excellent Stripe API, so this will be familiar to a lot of you. You can paginate using either `starting_after` or `ending_before` (but not both at once):
- When you use `starting_after`, we sort records in chronological order, allowing you to page from the start of time up until the current day. - When you use `ending_before`, we sort records in reverse chronological order, effectively working backwards from the object ID you give us. Every list response is wrapped in a list object. The `has_more` boolean tells you whether another page is available, and the value to pass as your next cursor is the `id` of the first or last item in `data`. All list endpoints accept these query string parameters:
- `limit`: the number of objects to return, between 1 and 100 (default 10). - `starting_after`: an object ID that defines your place in the list, paging forwards. - `ending_before`: an object ID that defines your place in the list, paging backwards. `GET /v1/aggregations` is not a list endpoint, does not use this pagination, and has its own `limit` (default 500 when grouping, max 1000). ## Errors
The Fathom API uses conventional HTTP status codes to indicate the result of a request. Codes in the `2xx` range mean success, codes in the `4xx` range indicate a problem with the information you supplied (a missing parameter, an invalid token, a permissions issue, and so on), and codes in the `5xx` range indicate an error on our side (these are rare).'
termsOfService: https://usefathom.com/legal/guidelines
contact:
name: Fathom Analytics Support
email: support@usefathom.com
url: https://usefathom.com
servers:
- url: https://api.usefathom.com/v1
description: Fathom Analytics API v1
security:
- bearerAuth: []
tags:
- name: Milestones
description: Annotate your reports with important dates.
paths:
/sites/{site_id}/milestones:
get:
operationId: listMilestones
summary: List milestones
description: 'Return a list of all milestones this site owns. Milestones are sorted by `created_at` ascending to allow you to paginate with ease.
**Permissions:** Requires read access to the site (`all-sites-readonly`, `read:{site_id}` or `manage:{site_id}`).
**Returns:** A list of milestone objects.'
tags:
- Milestones
parameters:
- name: site_id
in: path
required: true
description: 'The ID of the site. This is the same string you use in your tracking code.
Example: `CDBUGS`'
schema:
type: string
example: CDBUGS
example: CDBUGS
- name: limit
in: query
required: false
description: A limit on the number of objects to be returned, between 1 and 100.
schema:
type: integer
default: 10
example: 10
- name: starting_after
in: query
required: false
description: A cursor for use in pagination. `starting_after` is an object ID that defines your place in the list. For example, if you make a list request and receive 10 objects ending with `obj_foo`, your subsequent call can include `starting_after=obj_foo` to fetch the next page.
schema:
type: string
- name: ending_before
in: query
required: false
description: A cursor for use in pagination, working in the opposite direction to `starting_after`. `ending_before` is an object ID that defines your place in the list.
schema:
type: string
responses:
'200':
description: Successful response
content:
application/json:
example:
object: list
url: /api/v1/CDBUGS/milestones
has_more: false
data:
- id: ddc9cdff-ab83-41fa-96c6-dfb276a862e7
object: milestone
name: Website Redesign Launch
milestone_date: '2024-01-15 00:00:00'
created_at: '2024-01-10 12:00:00'
updated_at: '2024-01-10 12:00:00'
- id: 7c3a1b29-5d64-4e0a-b1f2-9a8c7e6d5f43
object: milestone
name: Marketing Campaign Start
milestone_date: '2024-02-01 00:00:00'
created_at: '2024-01-20 09:30:00'
updated_at: '2024-01-20 09:30:00'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
post:
operationId: createMilestone
summary: Create milestone
description: 'Create a milestone. Returns HTTP `201 Created` on success.
**Permissions:** Requires write access to the site (`manage:{site_id}`).
**Returns:** A milestone object.'
tags:
- Milestones
parameters:
- name: site_id
in: path
required: true
description: 'The ID of the site. This is the same string you use in your tracking code.
Example: `CDBUGS`'
schema:
type: string
example: CDBUGS
example: CDBUGS
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
example: Website Redesign Launch
description: The name of the milestone (up to 30 characters).
milestone_date:
type: string
example: '2024-01-15'
description: The date of the milestone in `YYYY-MM-DD` format.
required:
- name
- milestone_date
responses:
'201':
description: Successful response
content:
application/json:
example:
id: ddc9cdff-ab83-41fa-96c6-dfb276a862e7
object: milestone
name: Website Redesign Launch
milestone_date: '2024-01-15 00:00:00'
created_at: '2024-01-10 12:00:00'
updated_at: '2024-01-10 12:00:00'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/sites/{site_id}/milestones/{milestone_id}:
get:
operationId: getMilestone
summary: Get milestone
description: 'Return a single milestone.
**Permissions:** Requires read access to the site (`all-sites-readonly`, `read:{site_id}` or `manage:{site_id}`).
**Returns:** A milestone object.'
tags:
- Milestones
parameters:
- name: site_id
in: path
required: true
description: 'The ID of the site. This is the same string you use in your tracking code.
Example: `CDBUGS`'
schema:
type: string
example: CDBUGS
example: CDBUGS
- name: milestone_id
in: path
required: true
description: The `id` (UUID) of the milestone you wish to retrieve.
schema:
type: string
example: ddc9cdff-ab83-41fa-96c6-dfb276a862e7
example: ddc9cdff-ab83-41fa-96c6-dfb276a862e7
responses:
'200':
description: Successful response
content:
application/json:
example:
id: ddc9cdff-ab83-41fa-96c6-dfb276a862e7
object: milestone
name: Website Redesign Launch
milestone_date: '2024-01-15 00:00:00'
created_at: '2024-01-10 12:00:00'
updated_at: '2024-01-10 12:00:00'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
post:
operationId: updateMilestone
summary: Update milestone
description: 'Update a milestone. Both `name` and `milestone_date` are required.
**Permissions:** Requires write access to the site (`manage:{site_id}`).
**Returns:** A milestone object.'
tags:
- Milestones
parameters:
- name: site_id
in: path
required: true
description: 'The ID of the site. This is the same string you use in your tracking code.
Example: `CDBUGS`'
schema:
type: string
example: CDBUGS
example: CDBUGS
- name: milestone_id
in: path
required: true
description: The `id` (UUID) of the milestone you wish to update.
schema:
type: string
example: ddc9cdff-ab83-41fa-96c6-dfb276a862e7
example: ddc9cdff-ab83-41fa-96c6-dfb276a862e7
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
example: Website Redesign Launch v2
description: The name of the milestone (up to 30 characters).
milestone_date:
type: string
example: '2024-01-20'
description: The date of the milestone in `YYYY-MM-DD` format.
required:
- name
- milestone_date
responses:
'200':
description: Successful response
content:
application/json:
example:
id: ddc9cdff-ab83-41fa-96c6-dfb276a862e7
object: milestone
name: Website Redesign Launch v2
milestone_date: '2024-01-20 00:00:00'
created_at: '2024-01-10 12:00:00'
updated_at: '2024-01-10 12:30:00'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
delete:
operationId: deleteMilestone
summary: Delete milestone
description: 'Delete a milestone. Careful: you can''t undo this, and neither can we.
**Permissions:** Requires write access to the site (`manage:{site_id}`).
**Returns:** Returns a deleted object on success. Otherwise, this call returns an error.'
tags:
- Milestones
parameters:
- name: site_id
in: path
required: true
description: 'The ID of the site. This is the same string you use in your tracking code.
Example: `CDBUGS`'
schema:
type: string
example: CDBUGS
example: CDBUGS
- name: milestone_id
in: path
required: true
description: The `id` (UUID) of the milestone you wish to delete.
schema:
type: string
example: ddc9cdff-ab83-41fa-96c6-dfb276a862e7
example: ddc9cdff-ab83-41fa-96c6-dfb276a862e7
responses:
'200':
description: Successful response
content:
application/json:
example:
id: ddc9cdff-ab83-41fa-96c6-dfb276a862e7
object: milestone
deleted: true
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
components:
responses:
BadRequest:
description: 'Bad request. Returned for validation failures and other request problems. Send `Accept: application/json` to receive errors as JSON.'
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/Error'
- $ref: '#/components/schemas/ValidationError'
examples:
general:
summary: General error
value:
error: Something went wrong with your request.
validation:
summary: Validation error
value:
errors:
name:
- The name field is required.
Unauthorized:
description: Authentication failed — the API token is missing, malformed or invalid.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: Unauthenticated.
schemas:
ValidationError:
type: object
properties:
errors:
type: object
additionalProperties:
type: array
items:
type: string
description: Validation messages keyed by field name.
required:
- errors
Error:
type: object
properties:
error:
type: string
description: A human-readable error message.
required:
- error
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: 'Authenticate with a personal API token created at https://app.usefathom.com/api, sent as `Authorization: Bearer <token>`.'
externalDocs:
description: Human-readable API reference
url: https://usefathom.com/api/v1
x-provenance:
method: harvested
publisher: Fathom Analytics
source: https://usefathom.com/api/openapi.json
note: split by refine-openapis from the provider's own published document, verified by fetch (openapi/_original/*.fetch.json);
stamped by stamp-openapi-provenance.py (roadmap#793)