Skip to main content

API Examples

These examples show common tasks with curl. They authenticate with a user API token and use jq to read values from JSON responses.

Set the server URL and your token once, so you can copy the commands as they are:

export GEOPULSE_URL="http://localhost:8080"
export GEOPULSE_TOKEN="<your-api-token>"

If a request fails, the response body describes the error. See Errors for the format.

Get the timeline for a date range​

GET /api/v1/timeline returns the stays, trips, and data gaps between from and to. Both parameters are ISO-8601 instants in UTC, such as 2026-05-01T00:00:00Z.

curl -sS -G "$GEOPULSE_URL/api/v1/timeline" \
-H "X-API-Key: $GEOPULSE_TOKEN" \
--data-urlencode "from=2026-05-01T00:00:00Z" \
--data-urlencode "to=2026-05-31T23:59:59Z"

The response (abbreviated) looks like:

{
"userId": "3f6c1d2e-8a4b-4c5d-9e0f-1a2b3c4d5e6f",
"stays": [
{
"id": 1201,
"timestamp": "2026-05-01T06:12:40Z",
"locationName": "Home",
"city": "Kyiv",
"country": "Ukraine",
"stayDuration": 9360,
"latitude": 50.4501,
"longitude": 30.5234
}
],
"trips": [
{
"id": 845,
"timestamp": "2026-05-01T08:48:40Z",
"latitude": 50.4501,
"longitude": 30.5234,
"endLatitude": 50.4422,
"endLongitude": 30.5367,
"tripDuration": 1260,
"distanceMeters": 4870,
"movementType": "CAR"
}
],
"dataGaps": [],
"staysCount": 1,
"tripsCount": 1,
"dataGapsCount": 0
}

stayDuration and tripDuration are in seconds.

Get raw GPS points for a date range​

As JSON, page by page​

GET /api/v1/gps/points returns raw GPS points one page at a time. from and to accept ISO-8601 instants or plain dates (2026-05-01). Plain dates are read in UTC: from starts at 00:00:00 and to ends at 23:59:59.

ParameterDefaultDescription
page1Page number, starting at 1.
size50Points per page, from 1 to 1000.
sortBytimestampField to sort by.
sortDirectiondescasc or desc.
curl -sS -G "$GEOPULSE_URL/api/v1/gps/points" \
-H "X-API-Key: $GEOPULSE_TOKEN" \
--data-urlencode "from=2026-05-01" \
--data-urlencode "to=2026-05-31" \
--data-urlencode "page=1" \
--data-urlencode "size=1000" \
--data-urlencode "sortDirection=asc"
{
"items": [
{
"id": 982341,
"timestamp": "2026-05-01T00:00:12Z",
"coordinates": {
"lat": 50.4501,
"lng": 30.5234
},
"accuracy": 8.0,
"battery": 87.0,
"velocity": 0.0,
"altitude": 179.0,
"sourceType": "OWNTRACKS"
}
],
"page": 1,
"size": 1000,
"totalElements": 41250,
"totalPages": 42
}

As CSV, in one request​

GET /api/v1/gps/points/exports streams every matching point as CSV, without paging. It takes the same from, to, and filter parameters:

curl -sS -G "$GEOPULSE_URL/api/v1/gps/points/exports" \
-H "X-API-Key: $GEOPULSE_TOKEN" \
--data-urlencode "from=2026-05-01" \
--data-urlencode "to=2026-05-31" \
-o gps-points-may-2026.csv

Import a GPX or OwnTracks file​

Imports run in the background. Upload the file to POST /api/v1/imports as multipart/form-data, then check the import job until it finishes. You can run only one import at a time.

Form fieldRequiredDescription
fileYesThe file to import.
formatYesgpx or owntracks. Other formats: google-timeline, geojson, csv, geopulse.
optionsNoJSON import options, sent with the application/json content type. See below.

Accepted file types:

  • gpx: a single .gpx file, or a .zip archive of GPX files.
  • owntracks: a .json file in OwnTracks format.

Upload a GPX file​

curl -sS -X POST "$GEOPULSE_URL/api/v1/imports" \
-H "X-API-Key: $GEOPULSE_TOKEN" \
-F "file=@ride.gpx" \
-F "format=gpx"

Upload an OwnTracks file​

curl -sS -X POST "$GEOPULSE_URL/api/v1/imports" \
-H "X-API-Key: $GEOPULSE_TOKEN" \
-F "file=@owntracks-export.json" \
-F "format=owntracks"

Import options​

Use options to import only part of a file or to replace existing data:

curl -sS -X POST "$GEOPULSE_URL/api/v1/imports" \
-H "X-API-Key: $GEOPULSE_TOKEN" \
-F "file=@owntracks-export.json" \
-F "format=owntracks" \
-F 'options={"startTime":"2026-05-01T00:00:00Z","endTime":"2026-05-31T23:59:59Z","clearDataBeforeImport":true};type=application/json'
OptionDescription
startTime, endTimeImport only points between these ISO-8601 instants. Send both or neither. Without them, all points are imported.
clearDataBeforeImportDelete your existing data in the imported date range before importing. Defaults to false.

Check the import status​

The upload response contains the job ID:

{
"importJobId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"status": "validating",
"phase": "validating",
"uploadedFileName": "ride.gpx",
"fileSizeBytes": 482113,
"progress": 0
}

Poll GET /api/v1/imports/{importJobId} until status is completed or failed:

job_id=$(curl -sS -X POST "$GEOPULSE_URL/api/v1/imports" \
-H "X-API-Key: $GEOPULSE_TOKEN" \
-F "file=@ride.gpx" \
-F "format=gpx" | jq -r '.importJobId')

while true; do
job=$(curl -sS "$GEOPULSE_URL/api/v1/imports/$job_id" -H "X-API-Key: $GEOPULSE_TOKEN")
status=$(echo "$job" | jq -r '.status')
echo "$status $(echo "$job" | jq -r '.phase') $(echo "$job" | jq -r '.progress')%"
case "$status" in
completed) break ;;
failed) echo "$job" | jq '.error'; exit 1 ;;
esac
sleep 5
done

After the points are imported, GeoPulse regenerates the timeline for the imported period. The phase field shows the current step, such as importing or timeline_generation.

Large files

A reverse proxy in front of GeoPulse, such as Cloudflare or nginx, may reject large uploads. If it does, raise its upload limit or call the backend directly. The web app works around such limits by uploading files larger than 80 MB in chunks through internal endpoints.

Export to GPX or OwnTracks​

Exports also run in the background. Create an export job with POST /api/v1/exports, wait until it completes, then download the file.

The request body:

FieldRequiredDescription
formatNogpx or owntracks. Other formats: geojson, csv, geopulse (default).
startTimeYesStart of the range, as an ISO-8601 instant.
endTimeYesEnd of the range, as an ISO-8601 instant.
gpxLayoutNoGPX only. See GPX layouts.
owntracksLayoutNoOwnTracks only. See OwnTracks layouts.
dataTypesNogeopulse only: data to include in the archive. Defaults to ["rawgps"]. Other formats ignore it and always export raw GPS points.

Create a GPX export​

curl -sS -X POST "$GEOPULSE_URL/api/v1/exports" \
-H "X-API-Key: $GEOPULSE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"format": "gpx",
"startTime": "2026-05-01T00:00:00Z",
"endTime": "2026-05-31T23:59:59Z"
}'

gpxLayout controls how the export is packaged:

gpxLayoutResult
single (default)One GPX file with all trips and stays.
zip-per-tripA ZIP archive with one GPX file per trip or stay.
zip-per-dayA ZIP archive with one GPX file per day.

For example, add "gpxLayout": "zip-per-day" to the body above.

Create an OwnTracks export​

curl -sS -X POST "$GEOPULSE_URL/api/v1/exports" \
-H "X-API-Key: $GEOPULSE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"format": "owntracks",
"startTime": "2026-05-01T00:00:00Z",
"endTime": "2026-05-31T23:59:59Z"
}'

owntracksLayout controls the JSON shape:

owntracksLayoutResult
ocat (default){"locations": [...]}, the format OwnTracks Recorder's ocat produces.
arrayA plain JSON array of location messages.

Wait for the export and download it​

The create response contains the job ID:

{
"exportJobId": "a3bb189e-8bf9-3888-9912-ace4e6543002",
"format": "gpx",
"status": "processing",
"progress": 0,
"dataTypes": ["rawgps"],
"startTime": "2026-05-01T00:00:00Z",
"endTime": "2026-05-31T23:59:59Z"
}

Poll GET /api/v1/exports/{exportJobId} until status is completed, then download the file from GET /api/v1/exports/{exportJobId}/content. The completed job also includes this path in downloadUrl, and expiresAt shows when the download expires.

job_id=$(curl -sS -X POST "$GEOPULSE_URL/api/v1/exports" \
-H "X-API-Key: $GEOPULSE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"format": "gpx",
"startTime": "2026-05-01T00:00:00Z",
"endTime": "2026-05-31T23:59:59Z"
}' | jq -r '.exportJobId')

while true; do
job=$(curl -sS "$GEOPULSE_URL/api/v1/exports/$job_id" -H "X-API-Key: $GEOPULSE_TOKEN")
status=$(echo "$job" | jq -r '.status')
echo "$status $(echo "$job" | jq -r '.progress')%"
case "$status" in
completed) break ;;
failed) echo "$job" | jq '.error'; exit 1 ;;
esac
sleep 5
done

# -OJ saves the file under the name the server sends, for example geopulse-gpx-export-3f6c1d2e-1780000000.gpx
curl -sS -OJ "$GEOPULSE_URL/api/v1/exports/$job_id/content" -H "X-API-Key: $GEOPULSE_TOKEN"

To choose the file name yourself, replace -OJ with -o may-2026.gpx (or .json for OwnTracks, .zip for a GPX ZIP export).