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:

shell
$ curl -sH 'Authorization: Bearer gfn_...' https://geofeed.network/api/v1/prefixes

Write 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.

shell
$ curl -sH 'Authorization: Bearer gfn_...' 'https://geofeed.network/api/v1/prefixes?asn=64512'
responsejson
{
  "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:

shell
$ 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:

shell
$ 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:

responsejson
{
  "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.

shell
$ curl -sD- -o feed.csv -H 'Authorization: Bearer gfn_...' https://geofeed.network/api/v1/feed
x-geofeed-lines: 2
x-geofeed-skipped: 1

X-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 asn in the payload.
400 missing_payload
Neither prefixes nor geofeed was 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:

shell
$ 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-0001

8The 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 objectRPSL
inetnum:        192.0.2.0 - 192.0.2.255
netname:        EXAMPLE-NET
country:        ES
geofeed:        https://geofeed.network/geofeeds/example.csv

Putting 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.