GoatCounter has a rudimentary API; this is far from feature-complete, but solves some common use cases.
The API is currently unversioned and prefixed with /api/v0
; breaking changes
will be avoided and are not expected but may occur. I’ll be sure to send ample
notification of this to everyone who has generated an API key.
To use the API create a key in your account (User → API
); send the API key in
the Authorization
header as Authorization: Bearer [token]
.
You will need to use Content-Type: application/json
; all requests return JSON
unless noted otherwise.
Example:
curl -X POST https://example.goatcounter.com/api/v0/export \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer 2q2snk7clgqs63tr4xc5bwseajlw88qzilr8fq157jz3qxwwmz5'
Replace the key and URL with your actual values.
The rate limit is 60 requests per 120 seconds. The current rate limits are indicated in the the headers:
X-Rate-Limit-Limit Number of requests the rate limit kicks in; this is always the same.
X-Rate-Limit-Remaining Requests remaining this period.
X-Rate-Limit-Reset Seconds until the rate limits resets.
Errors are reported in either an error
or errors
field; the error
field
always contains a string; for example:
{
"error": "oh noes!"
}
The errors
field contains an object with a list:
{
"errors": {
"key": ["error1", "error2"],
"another": ["oh noes!"]
}
}
A status code in the 2xx
range will never contain errors, a status code in the
4xx
or 5xx
range will always have either error
or errors
, but never
both. There may also be additional data in other fields on errors.
API reference docs are available at:
You can use /api/v0/count
to send requests from your backend. This is the same
as /count
but has higher rate-limits, allows setting some additional fields,
and allows batching multiple pageviews in one request.
Detail are available in the API reference, a simple example might look like:
#!/bin/sh
token=\[your api token]
api=https://\[my code].goatcounter.com/api/v0
curl() {
\command curl \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $token" \
"$@"
}
curl -X POST "$api/count" \
--data '{"no_sessions": true, "hits": [{"path": "/one"}, {"path": "/two"}]}'
Example to export via the API:
#!/bin/sh
token=\[your api token]
api=https://\[my code].goatcounter.com/api/v0
curl() {
\command curl \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $token" \
"$@"
}
# Start a new export, get ID from response object.
id=$(curl -X POST "$api/export" | jq .id)
# The export is started in the background, so we'll need to wait until it's finished.
while :; do
sleep 1
finished=$(curl "$api/export/$id" | jq .finished_at)
if [ "$finished" != "null" ]; then
# Download the export.
curl "$api/export/$id/download" | gzip -d
break
fi
done
The above doesn’t does no error checking for brevity: errors are reported in the
error
or errors
field as described above.
The export object contains a last_hit_id
parameter, which can be used as a
pagination cursor to only download hits after this export. This is useful to
sync your local database every hour or so:
# Get cursor
start=$(curl "$api/export/$id" | jq .last_hit_id)
# Start new export starting from the cursor.
id=$(curl -X POST "$api/export" --data "{\"start_from_hit_id\":$start}" | jq .id)
Feel free to get in touch if you’ve got any questions or having any problems; a lot of times they can be resolved without too much problems.
Ways to contact me: show