GP codes
A GP code is a short string that resolves to a place. Each code has a public page at https://gpcode.com/<code> and a QR image that encodes it. You can manage every code you created through the API.
Addressing a codeEndpoints take the code string in the path: /gp-codes/12345. Codes are unique per country, so if you hold the same custom string in more than one country, add ?country=MT — otherwise you get 409 ambiguous_code with the list of countries.
The GP code object
GP Code's id for the code.
The code string, e.g. 12345 or ACMEDEPOT01.
standard (assigned from the GP Code pool) or custom (a string you chose).
One summary value, first match wins:
Outside its validity period.
Custom code awaiting GP Code review.
Switched off because the allowance was exceeded.
is_active is false.
Live and resolving.
Short place name, e.g. "Front door". Defaults to the code itself.
Full address — as you sent it, or found by reverse geocoding.
Coordinates in decimal degrees.
{ code, name } — ISO alpha-2 code and English name.
Who the code belongs to.
user — one of your users · partner — your partner account · personal — the customer moved it to their own GP Code account (read-only for you).
Set when kind is user.
Your reference of the owning customer.
Optional postal and contact details, all free text or null: zip_code, city, street, house_number, place_number, phone_country_code, phone_number.
Your on/off switch for the code.
False while a custom code awaits review.
True when switched off because the allowance was exceeded.
Validity period. New codes are valid from creation for the number of days in your partner settings (365 by default).
Public page of the code. This is what the QR encodes.
Timestamps.
Only on single-code responses (create, retrieve), unless ?qr=false.
Always png.
The URL in the QR.
400×400 PNG as a base64 data URL, ready for <img src>. Same image as on GP Code's printable PDF.
{
"id": 91733,
"code": "12345",
"type": "standard",
"status": "active",
"location": "Front door",
"address": "Triq il-Kbira 5, Valletta",
"lat": 35.8989,
"lng": 14.5146,
"country": { "code": "MT", "name": "Malta" },
"owner": {
"kind": "user",
"user_id": 8842,
"external_ref": "cust-4417"
},
"details": {
"zip_code": null,
"city": "Valletta",
"street": null,
"house_number": "5",
"place_number": null,
"phone_country_code": null,
"phone_number": null
},
"is_active": true,
"approved": true,
"quota_suspended": false,
"valid_from": "2026-11-01T10:00:00+00:00",
"valid_to": "2027-11-01T10:00:00+00:00",
"url": "https://gpcode.com/12345",
"created_at": "2026-11-01T10:00:00+00:00",
"updated_at": "2026-11-01T10:00:00+00:00"
}
Create a code
Creates one standard or custom code from coordinates, an address or both, and books it against your allowance.
- Standard codes take the next code from the GP Code pool and are
activeat once. - Custom codes need
gp_code. The string is checked for availability in its country, against the reserved list and by content screening. The code starts aspending_approvaland goes live once GP Code approves it.
Location
lat+lng: the pin decides the country.addressis stored as sent; without it, it is filled by reverse geocoding.addressonly: geocoded to coordinates;422 address_not_foundif that fails.countrynarrows the address search and is the fallback when the pin's country cannot be determined.
Owner
user: { external_ref, name }— the user is found or created (user_createdin the response tells you which);user_id— one of your existing users (404otherwise);- neither — the code belongs to your partner account.
Headers
Recommended. Your unique id for this creation (max. 191). For 24 h a retry with the same key returns the first code with 200. See Idempotency.
Query parameters
Default true. false leaves the QR out of the response.
Body parameters
standard or custom.
Required for custom, not allowed for standard. 4–30 letters and digits (A–Z, 0–9).
Required together, unless address is sent. −90…90 and −180…180.
Required unless lat/lng are sent. Max. 500.
ISO 3166-1 alpha-2 hint, e.g. MT. The pin's own country wins.
Short place name, max. 500. Defaults to the code.
Owner by your reference. Not together with user_id.
Max. 191, no @.
Max. 250. Used only when the user is created.
Owner by id; must be one of your users. Not together with user.
Max. 30.
Max. 255 each.
Max. 50 each.
Max. 16, e.g. +356.
Max. 50.
Detail fields are sent at the top level and come back under details.
Returns
201 with the GP code plus user_created (boolean) and qr. 200 with the same shape for an idempotent replay.
| Status | Error code |
|---|---|
402 | quota_exhausted — nothing was written |
404 | not_found — user_id is not one of yours |
409 | code_unavailable, code_protected, code_rejected, request_in_progress |
422 | invalid_request, address_not_found, country_unresolved |
503 | pool_depleted, client_misconfigured |
# Standard code from a map pin, for a new or existing user
curl {{BASE}}/gp-codes \
-H "X-GoPeeko-Key: $GPCODE_KEY" \
-H "Authorization: Bearer $GPCODE_SECRET" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-4417-a" \
-d '{
"type": "standard",
"lat": 35.8989,
"lng": 14.5146,
"location": "Front door",
"house_number": "5",
"user": { "external_ref": "cust-4417", "name": "Depot 4417" }
}'
// Standard code from a map pin, for a new or existing user
$res = $client->post('gp-codes', [
'headers' => ['Idempotency-Key' => 'order-4417-a'],
'json' => [
'type' => 'standard',
'lat' => 35.8989,
'lng' => 14.5146,
'location' => 'Front door',
'house_number' => '5',
'user' => ['external_ref' => 'cust-4417', 'name' => 'Depot 4417'],
],
]);
$code = json_decode($res->getBody(), true)['data'];
file_put_contents("{$code['code']}.png", base64_decode(explode(',', $code['qr']['data_url'])[1]));
// Standard code from a map pin, for a new or existing user
const res = await fetch(`${BASE}/gp-codes`, {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json', 'Idempotency-Key': 'order-4417-a' },
body: JSON.stringify({
type: 'standard',
lat: 35.8989,
lng: 14.5146,
location: 'Front door',
house_number: '5',
user: { external_ref: 'cust-4417', name: 'Depot 4417' },
}),
});
const { data: code } = await res.json();
const png = Buffer.from(code.qr.data_url.split(',')[1], 'base64');
# Standard code from a map pin, for a new or existing user
import base64
res = session.post(
f"{BASE}/gp-codes",
headers={"Idempotency-Key": "order-4417-a"},
json={
"type": "standard",
"lat": 35.8989,
"lng": 14.5146,
"location": "Front door",
"house_number": "5",
"user": {"external_ref": "cust-4417", "name": "Depot 4417"},
},
)
code = res.json()["data"]
png = base64.b64decode(code["qr"]["data_url"].split(",", 1)[1])
// From an address, owned by your partner account
{
"type": "standard",
"address": "Triq il-Kbira 12, Birkirkara",
"country": "MT"
}
// Custom code for an existing user
{
"type": "custom",
"gp_code": "ACMEDEPOT01",
"lat": 35.8971,
"lng": 14.4611,
"user_id": 8842
}
{
"data": {
"id": 91733,
"code": "12345",
"type": "standard",
"status": "active",
"location": "Front door",
"address": "Triq il-Kbira 5, Valletta",
"lat": 35.8989,
"lng": 14.5146,
"country": { "code": "MT", "name": "Malta" },
"owner": { "kind": "user", "user_id": 8842, "external_ref": "cust-4417" },
"details": { "zip_code": null, "city": null, "street": null, "house_number": "5", "place_number": null, "phone_country_code": null, "phone_number": null },
"is_active": true,
"approved": true,
"quota_suspended": false,
"valid_from": "2026-11-01T10:00:00+00:00",
"valid_to": "2027-11-01T10:00:00+00:00",
"url": "https://gpcode.com/12345",
"created_at": "2026-11-01T10:00:00+00:00",
"updated_at": "2026-11-01T10:00:00+00:00",
"user_created": true,
"qr": {
"format": "png",
"encodes": "https://gpcode.com/12345",
"data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA…"
}
}
}
List codes
Every code you created, newest first, paginated. Deleted codes are excluded. Lists carry no QR; retrieve a code for its QR.
Query parameters
Only codes owned by this user of yours.
Only codes created for the customer with this reference.
standard or custom.
ISO alpha-2, e.g. MT.
Default 1.
1–100. Default 25.
Returns
200 with data (array of GP codes) and meta.
curl "{{BASE}}/gp-codes?external_ref=cust-4417&type=standard" \
-H "X-GoPeeko-Key: $GPCODE_KEY" \
-H "Authorization: Bearer $GPCODE_SECRET"
$res = $client->get('gp-codes', [
'query' => ['external_ref' => 'cust-4417', 'type' => 'standard'],
]);
$codes = json_decode($res->getBody(), true)['data'];
const qs = new URLSearchParams({ external_ref: 'cust-4417', type: 'standard' });
const res = await fetch(`${BASE}/gp-codes?${qs}`, { headers });
const { data: codes, meta } = await res.json();
body = session.get(
f"{BASE}/gp-codes",
params={"external_ref": "cust-4417", "type": "standard"},
).json()
codes, meta = body["data"], body["meta"]
{
"data": [
{
"id": 91733,
"code": "12345",
"type": "standard",
"status": "active",
"location": "Front door",
"country": { "code": "MT", "name": "Malta" },
"owner": { "kind": "user", "user_id": 8842, "external_ref": "cust-4417" },
"url": "https://gpcode.com/12345",
"…": "…"
}
],
"meta": { "current_page": 1, "per_page": 25, "last_page": 1, "total": 1 }
}
Retrieve a code
One of your codes, with its QR. Also returns codes your customer has since moved to their own GP Code account (owner.kind = personal); those are read-only.
Path parameters
The code string, letters and digits only.
Query parameters
Needed only when you hold the same string in several countries.
Default true. false leaves the QR out.
Returns
200 with the GP code plus qr. Errors: 404 not_found · 409 ambiguous_code.
curl {{BASE}}/gp-codes/12345 \
-H "X-GoPeeko-Key: $GPCODE_KEY" \
-H "Authorization: Bearer $GPCODE_SECRET"
$code = json_decode($client->get('gp-codes/12345')->getBody(), true)['data'];
echo '<img src="' . $code['qr']['data_url'] . '" alt="GP code ' . $code['code'] . '">';
const res = await fetch(`${BASE}/gp-codes/12345`, { headers });
const { data: code } = await res.json();
code = session.get(f"{BASE}/gp-codes/12345").json()["data"]
{
"data": {
"id": 91733,
"code": "12345",
"status": "active",
"url": "https://gpcode.com/12345",
"…": "…",
"qr": {
"format": "png",
"encodes": "https://gpcode.com/12345",
"data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA…"
}
}
}
Update a code
Send only what changes. The code string, type and owner cannot change.
- Move the code: send
lat+lngand/oraddress(same rules as on create). If the new location is in another country where the same string already exists:409 code_unavailable. - Details: a detail field sent as
nullor""is cleared; fields you leave out are kept. locationsent empty resets it to the code string.
Refused with 422 validity_expired outside the code's validity period and with 409 not_manageable when the customer has moved the code to their own GP Code account.
Query parameters
Needed only for an ambiguous code string.
Body parameters
Short place name, max. 500.
Switch the code on or off.
New pin. Together.
New address, max. 500.
ISO alpha-2 hint for the new location.
Detail fields, same limits as on create.
Returns
200 with the updated GP code (no QR — the QR never changes, since the code string doesn't).
Errors: 404 not_found · 409 ambiguous_code | code_unavailable | not_manageable · 422 invalid_request | address_not_found | country_unresolved | validity_expired.
curl -X PATCH {{BASE}}/gp-codes/12345 \
-H "X-GoPeeko-Key: $GPCODE_KEY" \
-H "Authorization: Bearer $GPCODE_SECRET" \
-H "Content-Type: application/json" \
-d '{ "location": "Back gate", "is_active": false, "city": "Valletta" }'
$res = $client->patch('gp-codes/12345', [
'json' => ['location' => 'Back gate', 'is_active' => false, 'city' => 'Valletta'],
]);
$code = json_decode($res->getBody(), true)['data'];
const res = await fetch(`${BASE}/gp-codes/12345`, {
method: 'PATCH',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({ location: 'Back gate', is_active: false, city: 'Valletta' }),
});
const { data: code } = await res.json();
code = session.patch(
f"{BASE}/gp-codes/12345",
json={"location": "Back gate", "is_active": False, "city": "Valletta"},
).json()["data"]
{
"data": {
"id": 91733,
"code": "12345",
"status": "inactive",
"location": "Back gate",
"details": { "city": "Valletta", "house_number": "5", "…": "…" },
"is_active": false,
"…": "…"
}
}
Delete a code
The code stops resolving immediately and its place in your allowance is freed. A deleted code no longer appears in lists or lookups.
Refused with 409 not_manageable when the customer has moved the code to their own GP Code account.
Query parameters
Needed only for an ambiguous code string.
Returns
204 No Content. Errors: 404 not_found · 409 ambiguous_code | not_manageable.
Deletion cannot be undone through the API. Printed QR codes for a deleted code stop working.
curl -X DELETE {{BASE}}/gp-codes/12345 \
-H "X-GoPeeko-Key: $GPCODE_KEY" \
-H "Authorization: Bearer $GPCODE_SECRET"
$client->delete('gp-codes/12345'); // 204
const res = await fetch(`${BASE}/gp-codes/12345`, { method: 'DELETE', headers });
console.log(res.status); // 204
res = session.delete(f"{BASE}/gp-codes/12345")
assert res.status_code == 204
# empty body