Catalog
Countries, states, cities, polygons, carriers, and pickups — the canonical place graph.
Countries → states → cities
/geo/countriescurl "https://apis.cloud.feeef.org/v1/geo/countries" \
-H "Authorization: Bearer $FEEEF_CLOUD_KEY"/geo/states?country=DZ&lang=fr{ "data": [ {
"code": "16", "country": "DZ", "name": "alger",
"displayName": "Alger",
"locales": { "ar": "الجزائر", "fr": "Alger", "en": "Algiers" },
"center": { "lat": 36.7538, "lng": 3.0588 },
"extentKm": { "width": 39.1, "height": 31.4 },
"h3": "871f1a4d2ffffff"
} ], "meta": { "attribution": "© OpenStreetMap contributors (ODbL)" } }code is the ISO-style two-digit wilaya code (01–58) — stable, and the
right thing to store as a foreign key. name is a plain-ASCII slug form;
displayName follows ?lang=.
/geo/cities?country=DZ&state=16&lang=arSame shape as a state, plus postalCode (nullable; availability depends on the dataset)
and id, a stable composite key (DZ-16-bab-ezzouar) used everywhere else
in the API that needs to reference a specific city: polygons, pickups,
resolve results.
curl "https://apis.cloud.feeef.org/v1/geo/cities?country=DZ&state=16&lang=ar" \
-H "Authorization: Bearer $FEEEF_CLOUD_KEY"Boundary polygons
/geo/cities/:cityId/polygon?level=displayKept out of the base city payload — most callers never need geometry, and a
full polygon can be large. level=display is simplified for map
rendering; level=full keeps more vertices for spatial calculations.
curl "https://apis.cloud.feeef.org/v1/geo/cities/DZ-16-bab-ezzouar/polygon?level=display" \
-H "Authorization: Bearer $FEEEF_CLOUD_KEY"{ "data": { "type": "Feature",
"properties": { "cityId": "DZ-16-bab-ezzouar", "level": "display", "areaKm2": 8.9 },
"geometry": { "type": "MultiPolygon", "coordinates": [ "…" ] } } }Carriers and pickups
/geo/couriers?country=DZThe carrier registry: which providers cover this country and what each one actually supports — create, cancel, edit, labels, pickups, webhooks, and batch size limits. This is what Parcels reads to decide whether an operation is even possible before trying it, and it's the same list an integration should check before assuming a carrier can do something (e.g. not every carrier can edit a submitted parcel).
/geo/pickups?country=DZ&courier=yalidine&state=16Cross-carrier stop-desk points, keyed to the same canonical city.id the
catalog uses. If you're building a stop-desk picker for checkout: resolve
the customer's address first (Resolve), then filter
pickups by the resolved city, not by matching the pickup's own free-text city
name against anything.
/geo/couriers/:id/cities?state=16&hasPickup=trueWhich communes a courier accepts stop-desk parcels for. Filter server-side
with hasPickup=true rather than fetching the whole wilaya and checking in your
own code, and use city=<name> when you only need one commune.
A desk nearby does not mean stop-desk is accepted
These are two different questions and carriers answer them differently.
hasPickup is the one their order API enforces: submit a stop-desk parcel to a
commune where it is false and the carrier rejects it — for Ecotrack, Stop desk non disponible pour cette commune — after the call is already spent.
The desk list is not that signal. 48hr has an agency in Hussein Dey and refuses
stop-desk parcels addressed there, while accepting them for Mohammadia where it
has no agency at all. Check hasPickup on the destination commune, then
let the customer choose a desk.
GET /geo/carriers remains a deprecated compatibility alias. New integrations should use /geo/couriers; the repository SDK's geo.carriers helper currently uses the alias.