API reference
Read and replace your prefixes from an IPAM or a deploy pipeline. Bearer-token authenticated, JSON in, JSON out, 120 requests per minute.
Last updated Sep 9, 2026
1Base URL and versioning
Every endpoint lives under https://geofeed.network/api/v1. The version is in the path and will stay where it is: a breaking change gets /api/v2 rather than a new field's meaning.
Requests and responses are JSON, except the feed endpoint, which returns the geofeed itself as text/csv. All endpoints require a token; none of them are public.
- GET /api/v1/prefixes
- Read your ASNs and every prefix on them.
- PUT /api/v1/prefixes
- Replace the prefixes of one ASN. Needs a write token.
- GET /api/v1/feed
- The rendered geofeed, as bytes.
2Authentication
Create a token in the panel, under Settings. It is shown once, at creation, and stored here only as a SHA-256 hash — if you lose it, generate another. Tokens start with gfn_ so a leaked one is recognisable in a log or a repository.
Send it as a bearer token:
$ curl -sH 'Authorization: Bearer gfn_...' https://geofeed.network/api/v1/prefixesWrite access is off by default and is granted per token. A token created without it can read everything and change nothing, which is what a monitoring script should hold. Revoking a token takes effect on the very next request; there is no cache to wait out.
The limit is 120 requests per minute per token. Over it, you get 429 with rate_limited.
3GET /api/v1/prefixes
Returns your feed id, your ASNs with a count each, and every prefix. Add ?asn=64512 to narrow it to one ASN.
$ curl -sH 'Authorization: Bearer gfn_...' 'https://geofeed.network/api/v1/prefixes?asn=64512'{
"geofeed_id": "a1b2c3d4e5f6",
"asns": [
{ "asn": 64512, "description": "Example Net", "prefixes": 2 }
],
"prefixes": [
{
"network": "192.0.2.0/24",
"asn": 64512,
"country": "ES",
"region": "Madrid",
"region_code": "ES-M",
"city": "Madrid",
"postal": "28001",
"published": true
},
{
"network": "203.0.113.0/24",
"asn": 64512,
"country": null,
"region": null,
"region_code": null,
"city": null,
"postal": null,
"published": false
}
]
}published is the field to watch. A prefix with no country is not written to the geofeed at all — publishing a location with no country would be publishing a guess — so it sits in your account, invisible to consumers, until you give it one. published: false is the only signal that a prefix you added is not actually being served.
4PUT /api/v1/prefixes
Replaces the prefixes of one ASN. A replace and not a patch, because that is what a source of truth wants to say: *these are the prefixes now*. Anything on that ASN and absent from the payload is deleted; other ASNs are untouched.
Two payload shapes are accepted. Send an array if an IPAM is generating it:
$ curl -X PUT https://geofeed.network/api/v1/prefixes \
-H 'Authorization: Bearer gfn_...' \
-H 'Content-Type: application/json' \
-d '{"asn": 64512, "prefixes": [
{"network": "192.0.2.0/24", "country": "ES", "region": "ES-M", "city": "Madrid", "postal": "28001"},
{"network": "2001:db8::/32", "country": "ES"}
]}'Or send a geofeed as text, which is what you already have if you publish one elsewhere. Region names are accepted here and resolved to ISO 3166-2 codes, because that is what existing feeds contain:
$ curl -X PUT https://geofeed.network/api/v1/prefixes \
-H 'Authorization: Bearer gfn_...' \
-H 'Content-Type: application/json' \
-d '{"asn": 64512, "geofeed": "192.0.2.0/24,ES,ES-M,Madrid,28001\n2001:db8::/32,ES,,,"}'The response is a count of what changed, so a pipeline can assert on it:
{
"asn": 64512,
"added": 2,
"updated": 0,
"deleted": 1,
"skipped": 0,
"unresolved_regions": 0
}skipped counts rows that could not be parsed or whose country is not a real ISO 3166-1 alpha-2 code. unresolved_regions counts region names that had no ISO 3166-2 match — those are stored as names and still published, but they are not what RFC 8805 asks for.
Limits: 5,000 prefixes per request, and the ASN has to exist on your account already. Add it in the panel first.
5GET /api/v1/feed
The exact bytes your published feed serves, rendered on demand. Useful in a deploy pipeline that wants to diff what is about to be published, and it works before you have published anything.
$ curl -sD- -o feed.csv -H 'Authorization: Bearer gfn_...' https://geofeed.network/api/v1/feed
x-geofeed-lines: 2
x-geofeed-skipped: 1X-Geofeed-Lines is what got written; X-Geofeed-Skipped is how many prefixes were left out for having no country. A non-zero skip count is worth failing a pipeline on.
Add ?asn=64512 for one ASN's feed. The region column follows your account's region-format setting, the same as the published file.
6Errors
Every error is JSON with an error code and sometimes a detail. The codes are stable; the detail text is not, so match on the code.
- 401 missing_token
- No Authorization header, or it was not a bearer token.
- 401 invalid_token
- The token does not exist or has been revoked.
- 403 read_only_token
- The token was created without write access.
- 404 unknown_asn
- That ASN is not on your account.
- 400 invalid_json
- The body was not JSON.
- 400 missing_asn
- No usable
asnin the payload. - 400 missing_payload
- Neither
prefixesnorgeofeedwas sent. - 400 too_many_prefixes
- Over 5,000 in one request.
- 429 rate_limited
- Over 120 requests in a minute for this token.
7The published feed
Your geofeed is served, without authentication, at https://geofeed.network/geofeeds/<your-id>.csv, and per-ASN at https://geofeed.network/geofeeds/<your-id>-asn<ASN>.csv. That is the URL you put in your registry object, and it is the only part of this service that third parties talk to.
It carries a strong ETag over the exact bytes and Cache-Control: public, max-age=3600, so a well-behaved poller revalidates rather than re-downloading. A conditional request that matches returns 304, and a 304 is a successful read: the consumer already holds your data.
A database failure returns 503, never 404. A 404 tells a consumer you have no geofeed, and some of them cache that. A 503 tells it to come back.
There is a permanent example feed you can fetch with no account at all, rendered by the same code as every real one:
$ curl -s https://geofeed.network/geofeeds/example.csv
# network,country,region,city,postal_code
192.0.2.0/24,ES,ES-M,Madrid,28001
198.51.100.0/24,US,US-NY,New York,10001
203.0.113.0/24,DE,,,
2001:db8::/32,ES,ES-B,Barcelona,08001
2001:db8:1::/48,JP,JP-13,Tokyo,100-00018The format, in one paragraph
RFC 8805 defines a geofeed as a CSV file with five columns — network,country,region,city,postal_code — one prefix per line, # for comments. Only the prefix and the country are meaningful; the other three are optional and a consumer that cannot use them ignores them.
The country is an ISO 3166-1 alpha-2 code. The region is an ISO 3166-2 subdivision code (ES-M), not a region name — that is the field most published feeds get wrong, and truncating the name to two letters produces codes that do not exist.
A country-only feed is valid, useful, and what most published feeds are. Start there.
9Discovery: how a provider finds your feed
Not from us. RFC 9092 says a consumer reads the geofeed: attribute of the inetnum or inet6num object covering the prefix — or a remarks: Geofeed <url> line where the registry has no dedicated attribute.
inetnum: 192.0.2.0 - 192.0.2.255
netname: EXAMPLE-NET
country: ES
geofeed: https://geofeed.network/geofeeds/example.csvPutting it on the aut-num instead is the most common mistake. Some consumers do look there; most do not. The panel can query RDAP and tell you whether the registry object covering each of your prefixes actually references your feed, references a different URL, or references nothing.

