# GoatCounter API documentation 0.1

## Endpoints

### count §

`POST /api/v0/count` Count pageviews. §

This can count one or more pageviews. Pageviews are not persisted immediately, but persisted in the background every 10 seconds.

The maximum amount of pageviews per request is 500.

Errors will have the key set to the index of the pageview. Any pageviews not listed have been processed and shouldn't be sent again.

#### Request body

- handlers.APICountRequest (application/json)

#### Responses

- `202 Accepted`

  202 Accepted (no data)

  (application/json)
- `400 Bad Request` handlers.apiError (application/json)
- `401 Unauthorized` handlers.authError (application/json)
- `403 Forbidden` handlers.authError (application/json)

### export §

`GET /api/v0/export/{id}` Get details about an export. §

#### Responses

- `200 OK` v2.Export (application/json)
- `400 Bad Request` handlers.apiError (application/json)
- `401 Unauthorized` handlers.authError (application/json)
- `403 Forbidden` handlers.authError (application/json)

`GET /api/v0/export/{id}/download` Download an export file. §

The export may take a while to generate, depending on the size. It will return a 202 Accepted status code if the export ID is still running.

Export files are kept for 24 hours, after which they're deleted. This will return a 400 Gone status code if the export has been deleted.

#### Responses

- `200 OK`

  200 OK (text/csv data)

  (text/csv)
- `202 Accepted` handlers.apiError (application/json)
- `400 Bad Request` handlers.apiError (application/json)
- `401 Unauthorized` handlers.authError (application/json)
- `403 Forbidden` handlers.authError (application/json)

`POST /api/v0/export` Start a new export in the background. §

This starts a new export in the background; this can only be done once an hour.

#### Request body

- handlers.apiExportRequest (application/json)

#### Responses

- `202 Accepted` v2.Export (application/json)
- `400 Bad Request` handlers.apiError (application/json)
- `401 Unauthorized` handlers.authError (application/json)
- `403 Forbidden` handlers.authError (application/json)

### paths §

`GET /api/v0/paths` Get an overview of paths on this site (without statistics). §

#### Query parameters

#### Responses

- `200 OK` handlers.apiPathsResponse (application/json)
- `400 Bad Request` handlers.apiError (application/json)
- `401 Unauthorized` handlers.authError (application/json)
- `403 Forbidden` handlers.authError (application/json)

### sites §

`GET /api/v0/sites` List all sites. §

#### Responses

- `200 OK` handlers.apiSitesResponse (application/json)
- `400 Bad Request` handlers.apiError (application/json)
- `401 Unauthorized` handlers.authError (application/json)
- `403 Forbidden` handlers.authError (application/json)

`GET /api/v0/sites/{id}` Get information about a site. §

Get all information about one site.

#### Responses

- `200 OK` goatcounter.Site (application/json)
- `400 Bad Request` handlers.apiError (application/json)
- `401 Unauthorized` handlers.authError (application/json)
- `403 Forbidden` handlers.authError (application/json)

`PATCH /api/v0/sites/{id}` Update a site. §

A POST request will *replace* the entire site with what's sent, blanking out any existing fields that may exist. A PATCH request will only update the fields that are sent.

#### Request body

- handlers.apiSiteUpdateRequest (application/json)

#### Responses

- `200 OK` goatcounter.Site (application/json)
- `400 Bad Request` handlers.apiError (application/json)
- `401 Unauthorized` handlers.authError (application/json)
- `403 Forbidden` handlers.authError (application/json)

`POST /api/v0/sites/{id}` Update a site. §

A POST request will *replace* the entire site with what's sent, blanking out any existing fields that may exist. A PATCH request will only update the fields that are sent.

#### Request body

- handlers.apiSiteUpdateRequest (application/json)

#### Responses

- `200 OK` goatcounter.Site (application/json)
- `400 Bad Request` handlers.apiError (application/json)
- `401 Unauthorized` handlers.authError (application/json)
- `403 Forbidden` handlers.authError (application/json)

`PUT /api/v0/sites` Create a new site. §

#### Request body

- goatcounter.Site (application/json)

#### Responses

- `200 OK` goatcounter.Site (application/json)
- `400 Bad Request` handlers.apiError (application/json)
- `401 Unauthorized` handlers.authError (application/json)
- `403 Forbidden` handlers.authError (application/json)

### stats §

`GET /api/v0/stats/hits` Get an overview of pageviews. §

#### Query parameters

#### Responses

- `200 OK` handlers.apiHitsResponse (application/json)
- `400 Bad Request` handlers.apiError (application/json)
- `401 Unauthorized` handlers.authError (application/json)
- `403 Forbidden` handlers.authError (application/json)

`GET /api/v0/stats/hits/{path_id}` Get an overview of referral information for a path. §

#### Query parameters

#### Responses

- `200 OK` handlers.apiRefsResponse (application/json)
- `400 Bad Request` handlers.apiError (application/json)
- `401 Unauthorized` handlers.authError (application/json)
- `403 Forbidden` handlers.authError (application/json)

`GET /api/v0/stats/total` Count total number of pageviews for a date range. §

This is mostly useful to display things like browser stats as a percentage of the total; the /api/v0/pages endpoint only counts the pageviews until it's paginated.

#### Query parameters

#### Responses

- `200 OK` handlers.apiCountTotalResponse (application/json)
- `400 Bad Request` handlers.apiError (application/json)
- `401 Unauthorized` handlers.authError (application/json)
- `403 Forbidden` handlers.authError (application/json)

`GET /api/v0/stats/{page}` Get browser/system/etc. stats. §

Page can be: browsers, systems, locations, languages, sizes, campaigns, toprefs.

#### Query parameters

#### Responses

- `200 OK` handlers.apiStatsResponse (application/json)
- `400 Bad Request` handlers.apiError (application/json)
- `401 Unauthorized` handlers.authError (application/json)
- `403 Forbidden` handlers.authError (application/json)

`GET /api/v0/stats/{page}/{id}` Get detailed stats for an ID. §

Page can be: browsers, systems, locations, sizes, campaigns, toprefs.

#### Query parameters

#### Responses

- `200 OK` handlers.apiStatsResponse (application/json)
- `400 Bad Request` handlers.apiError (application/json)
- `401 Unauthorized` handlers.authError (application/json)
- `403 Forbidden` handlers.authError (application/json)

### users §

`GET /api/v0/me` Get information about the current user and API key. §

#### Responses

- `200 OK` handlers.meResponse (application/json)
- `400 Bad Request` handlers.apiError (application/json)
- `401 Unauthorized` handlers.authError (application/json)
- `403 Forbidden` handlers.authError (application/json)

## Models

### goatcounter.APIToken §

#### name string

#### permissions integer

#### sites integer

### goatcounter.HitList §

#### count integer

Number of visitors for the selected date range.

#### path_id integer

Path ID

#### path string

Path name (e.g. /hello.html).

#### event boolean

Is this an event?

#### title string

Page title.

#### max integer

Highest visitors per hour or day (depending on daily being set).

#### stats array [type: goatcounter.HitListStat]

Statistics by day and hour.

#### ref_scheme string [enum: "enum:", "h", "g", "c", "o"]

What kind of referral this is; only set when retrieving referrals .

h HTTP Referal header. g Generated; for example are Google domains (google.com, google.nl, google.co.nz, etc.) are grouped as the generated referral "Google". c Campaign (via query parameter) o Other

### goatcounter.HitListStat §

#### day string [format: date]

Day these statistics are for.

#### hourly array [type: integer]

Visitors per hour.

#### daily integer

Total visitors for this day.

#### weekly integer

Visitors for the week; set once every 7 days. This value will not be set if it's 0.

#### monthly integer

Visitors for the month; set on first day of the month. This value will not be set if it's 0.

### goatcounter.HitStat §

#### id string

ID for selecting more details; not present in the detail view.

#### name string

Display name.

#### count integer

Number of visitors.

#### ref_scheme string [enum: "enum:", "h", "g", "c", "o"]

What kind of referral this is; only set when retrieving referrals .

h HTTP Referal header. g Generated; for example are Google domains (google.com, google.nl, google.co.nz, etc.) are grouped as the generated referral "Google". c Campaign (via query parameter) o Other

### goatcounter.Path §

#### id integer

Path ID

#### path string

Path name

#### title string

Page title

#### event boolean

Is this an event?

### goatcounter.Site §

#### id integer [readonly]

#### parent integer [readonly]

#### cname string

Custom domain, e.g. "stats.example.com".

When self-hosting this is the domain/vhost your site is accessible at.

#### cname_setup_at string [format: date-time] [readonly]

When the CNAME was verified.

#### code string

Domain code (e.g. "arp242", which makes arp242.goatcounter.com). Only used for goatcounter.com and not when self-hosting.

#### link_domain string

Site domain for linking (www.arp242.net). Note this can be a full URL and is a bit misnamed.

#### setttings

#### user_defaults

#### received_data boolean

Whether this site has received any data; will be true after the first pageview.

#### state string

#### created_at string [format: date-time]

#### updated_at string [format: date-time]

#### first_hit_at string [format: date-time]

### goatcounter.SiteSettings §

SiteSettings contains all the user-configurable settings for a site, with the exception of the domain settings. This is stored as JSON in the database.

#### public string

#### secret string

#### allow_counter boolean

#### allow_bosmang boolean

#### data_retention integer

#### ignore_ips array [type: string]

#### collect integer

#### collect_regions array [type: string]

#### allow_embed array [type: string]

### goatcounter.User §

#### id integer [readonly]

#### site integer [readonly]

#### email string

#### email_verified boolean [readonly]

#### totp_enabled boolean [readonly]

#### access [readonly]

#### login_at string [format: date-time] [readonly]

#### open_at string [format: date-time] [readonly]

#### reset_at string [format: date-time] [readonly]

#### settings

#### last_report_at string [format: date-time]

Keep track when the last email report was sent, so we don't double-send them.

#### created_at string [format: date-time] [readonly]

#### updated_at string [format: date-time] [readonly]

### goatcounter.UserAccess §

### goatcounter.UserSettings §

UserSettings are all user preferences.

#### twenty_four_hours boolean

#### sunday_starts_week boolean

#### language string

#### date_format string

#### number_format string

#### timezone

#### widgets array [type: goatcounter.Widget]

#### views array [type: goatcounter.View]

#### email_reports integer

#### fewer_numbers boolean

#### fewer_numbers_lock_until string [format: date-time]

#### theme string

#### datepicker boolean

### goatcounter.View §

Views for the dashboard; these settings apply to all widget and are configurable in the yellow box at the top.

#### name string

#### filter string

#### group integer

#### period string

"week", "month", etc., or n days: "8"

### goatcounter.Widget §

Widgets is a list of widgets to be printed, in order.

### handlers.APICountRequest §

#### no_sessions boolean

By default it's an error to send pageviews that don't have either a Session or UserAgent and IP set. This avoids accidental errors.

When this is set it will just continue without recording sessions for pageviews that don't have these parameters set.

#### filter array [type: string]

Filter pageviews; accepted values:

ip Ignore requests coming from IP addresses listed in "Settings → Ignore IP". Requires the IP field to be set.

["ip"] is used if this field isn't sent; send an empty array ([]) to not filter anything.

The X-Goatcounter-Filter header will be set to a list of indexes if any pageviews are filtered; for example:

X-Goatcounter-Filter: 5, 10

This header will be omitted if nothing is filtered.

#### hits array [type: handlers.APICountRequestHit]

Hits is the list of pageviews.

### handlers.APICountRequestHit §

#### path string [required]

Path of the pageview, or the event name.

#### title string

Page title, or some descriptive event title.

#### event boolean

Is this an event?

#### ref string

Referrer value, can be an URL (i.e. the Referal: header) or any string.

#### size array [type: number]

Screen width.

For compatibility it also accepts the size as "width,height,scaling", but the height and scaling are not used and this format is deprecated.

#### query string

Query parameters for this pageview, used to get campaign parameters.

#### bot integer

Hint if this should be considered a bot; should be one of the JSBot*` constants from isbot; note the backend may override this if it detects a bot using another method. https://github.com/zgoat/isbot/blob/master/isbot.go#L28

#### user_agent string

User-Agent header.

#### location string

Location as ISO-3166-1 alpha2 string (e.g. NL, ID, etc.)

#### ip string

IP to get location from; not used if location is set. Also used for session generation.

#### created_at string [format: date-time]

Time this pageview should be recorded at; this can be in the past, but not in the future.

#### session string

Normally a session is based on hash(User-Agent+IP+salt), but if you don't send the IP address then we can't determine the session.

In those cases, you can store your own session identifiers and send them along. Note these will not be stored in the database as the sessionID (just as the hashes aren't), they're just used as a unique grouping identifier.

### handlers.apiCountTotalRequest §

#### start string [format: date-time] [default: one week ago]

Start time, should be rounded to the hour.

#### end string [format: date-time] [default: current time]

End time, should be rounded to the hour.

#### include_paths array [type: string]

Include only these path IDs; default is to include everything.

If path_by_name is set, it will look up paths by name instead of ID.

#### path_by_name boolean

Get values for include_paths and exclude_paths by path name, rather than path ID. This is more convenient in some cases, but also a bit slower.

### handlers.apiCountTotalResponse §

#### total integer

Total number of visitors (including events).

#### total_events integer

Total number of visitors for events.

#### total_utc integer

Total number of visitors in UTC. The browser, system, etc, stats are always in UTC.

#### stats array [type: goatcounter.HitListStat]

Total overview per day and hour.

### handlers.apiError §

Generic API error. An error will have either the "error" or "errors" field set, but not both.

#### error string

#### errors

### handlers.apiExportRequest §

#### start_from_hit_id integer

Pagination cursor; only export hits with an ID greater than this.

### handlers.apiHitsRequest §

#### start string [format: date-time] [default: one week ago]

Start time, should be rounded to the hour.

#### end string [format: date-time] [default: current time]

End time, should be rounded to the hour.

#### daily boolean

Deprecated: identical to group=day and will be removed in the future.

#### group integer [enum: "enum:", "hour", "day", "week", "month"]

Set Max value in the response to the highest daily, weekly, or monthly value, instead of hourly.

The Hourly, Daily, Weekly, and Monthly values are always included in the response – this only affects the Max value, which is useful if you want to draw charts like the GoatCounter dashboard: you need to know the maximum Y-axis value of the chart to draw it.

#### include_paths array [type: string]

Include only these path IDs; default is to include everything.

If path_by_name is set, it will look up paths by name instead of ID.

#### exclude_paths array [type: string]

Exclude these path IDs, for pagination.

If path_by_name is set, it will look up paths by name instead of ID.

#### path_by_name boolean

Get values for include_paths and exclude_paths by path name, rather than path ID. This is more convenient in some cases, but also a bit slower.

#### limit integer [default: 20] [range: 1-100]

Maximum number of pages to get.

### handlers.apiHitsResponse §

#### hits array [type: goatcounter.HitList]

Sorted list of paths with their visitor and pageview count.

#### total integer

Total number of visitors in the returned result.

#### more boolean

More hits after this?

### handlers.apiPathsRequest §

#### Limit integer [default: 20] [range: 1-200]

Limit number of returned results

#### After integer

Only select paths after this ID, for pagination.

### handlers.apiPathsResponse §

#### paths array [type: goatcounter.Path]

List of paths, sorted by ID.

#### more boolean

True if there are more paths.

### handlers.apiRefsRequest §

#### start string [format: date-time] [default: one week ago]

Start time, should be rounded to the hour.

#### end string [format: date-time] [default: current time]

End time, should be rounded to the hour.

#### limit integer [default: 20] [range: 1-100]

Maximum number of pages to get.

#### offset integer

Offset for pagination.

### handlers.apiRefsResponse §

#### refs array [type: goatcounter.HitStat]

#### more boolean

### handlers.apiSiteUpdateRequest §

#### settings

#### cname string

#### link_domain string

### handlers.apiSitesResponse §

#### sites array [type: goatcounter.Site]

### handlers.apiStatsRequest §

#### start string [format: date-time] [default: one week ago]

Start time, should be rounded to the hour.

#### end string [format: date-time] [default: current time]

End time, should be rounded to the hour.

#### include_paths array [type: string]

Include only these path IDs; default is to include everything.

If path_by_name is set, it will look up paths by name instead of ID.

#### path_by_name boolean

Get values for include_paths and exclude_paths by path name, rather than path ID. This is more convenient in some cases, but also a bit slower.

#### limit integer [default: 20] [range: 1-100]

Maximum number of pages to get.

#### offset integer

Offset for pagination.

### handlers.apiStatsResponse §

#### stats array [type: goatcounter.HitStat]

Sorted list of paths with their visitor and pageview count.

#### more boolean

### handlers.authError §

Authentication error: the API key was not provided or incorrect.

#### Error string

### handlers.meResponse §

#### user

#### token

### time.Location §

A Location maps time instants to the zone in use at that time. Typically, the Location represents the collection of time offsets in use in a geographical area. For many Locations the time offset varies depending on whether daylight savings time is in use at the time instant. Location is used to provide a time zone in a printed Time value and for calculations involving intervals that may cross daylight savings time boundaries.

### tz.Zone §

Zone represents a time zone.

#### CountryCode string

ID

#### Zone string

Asia/Makassar

#### Abbr array [type: string]

WITA – the correct abbreviation may change depending on the time of year (i.e. CET and CEST, depending on DST).

#### CountryName string

Indonesia

#### Comments string

Borneo (east, south); Sulawesi/Celebes, Bali, Nusa Tengarra; Timor (west)

### v2.Export §

#### id integer [readonly]

#### site_id integer [readonly]

#### start_from_hit_id integer

The hit ID this export was started from.

#### last_hit_id integer [readonly]

Last hit ID that was exported; can be used as start_from_hit_id.

#### created_at string [format: date-time] [readonly]

#### finished_at string [format: date-time] [readonly]

#### num_rows integer [readonly]

#### size string [readonly]

File size in MB.

#### hash string [readonly]

SHA256 hash.

#### error string [readonly]

Any errors that may have occured.