API documentation

The GridTracker API serves two datasets as JSON: the interconnection queue across every power market GridTracker tracks, and data center facilities with the official records behind them. The base URL is https://api.gridtracker.io. Every endpoint except the health check needs a key.

Get an API key

Prefer an AI assistant? See the MCP connector

On this page

Endpoints

Each path links to its section of the guide below. A part in braces, such as {facilityId}, is an id from a list response.

MethodPathWhat it returns
Health
GET/healthWhether the service is up (no key needed)
Interconnection queue projects
GET/v2/transmission-grid-interconnection-queue-projectsA page of matching projects
GET/v2/transmission-grid-interconnection-queue-projects/{uniqueId}One project
Data center facilities
GET/v2/data-center-facilitiesA page of matching facilities
GET/v2/data-center-facilities/{facilityId}One facility, with its official records
GET/v2/data-center-facilities/aggregateCounts and capacity metrics over matching facilities, grouped
Data center official records
GET/v2/data-center-official-recordsA page of matching records
GET/v2/data-center-official-records/{recordId}One record

What the API is

The GridTracker public REST API returns two of GridTracker's datasets as JSON. The first is the interconnection queue: one record per project in a grid operator's interconnection queue, across every power market GridTracker tracks, from the interconnection.fyi dataset, refreshed once a day. The second is data centers: one record per data center facility in the US and Canada, and one per official record (a permit, a docket, a filing) behind those facilities, from a snapshot that refreshes on an irregular schedule (see The data center snapshot). You can filter either dataset, sort it, page through it, or fetch one item by its id, and you can aggregate facilities into counts and capacity totals.

The base URL is https://api.gridtracker.io, served over HTTPS only. Every endpoint except the health check needs an API key (see Authentication).

Once you have a key, this is the first request to try:

curl -s "https://api.gridtracker.io/v2/transmission-grid-interconnection-queue-projects?limit=1" \
  -H "X-API-Key: gtapi_<your key>"

Replace gtapi_<your key> with your own key in this example and in every other one in this guide. Left as it is, the placeholder gets a 401 with the code INVALID_API_KEY.

Most of the examples pipe their output through jq to keep it short. Drop the jq line to see the full response.

GET /health needs no key and is not versioned. It answers {"status":"ok"} while the service is up.

Versions

The version is the first segment of the path. The current version is /v2, and this guide describes it.

/v1 is retired. The first API ran at /v1 with different parameter names, a different response shape, a different pagination scheme and different limits. Version 2 does not honour that contract, and reusing the number would break callers without warning. Every request to a /v1 path now returns 410 Gone with the code API_VERSION_RETIRED and a Sunset header carrying the date the version was retired, 6 October 2026. Keys issued for /v1 do not work on /v2; GridTracker issues new keys to the people who held them.

Within a version, GridTracker only makes changes that cannot break a working client: a new endpoint, a new optional parameter, a new response field, a new value in a documented list. Ignore response fields you do not recognise. A field or parameter is never removed or renamed inside a version, a field's type or meaning never changes, and neither do defaults, validation rules or error codes. Any of those changes ships as a new version.

Two versions can run side by side, and your key and its limits work on all of them. When a version is going to be retired, it is announced at least six months ahead. From the day of the announcement every response from that version carries two headers: Deprecation (the announcement date as a Unix timestamp with an @ in front, as RFC 9745 specifies) and Sunset (the retirement date as an HTTP date, as RFC 8594 specifies). GridTracker also tells known key holders directly.

Authentication

Send your key in the X-API-Key header on every request:

X-API-Key: gtapi_<your key>

A key looks like gtapi_ followed by 12 lowercase letters and digits, a _, and 43 more characters: 62 characters in all. The 12 characters after gtapi_ are the key's lookup prefix. The prefix is not secret, and it is the right thing to quote when you write to support about a key.

A key is shown once, when GridTracker issues it, and cannot be shown again. Store it the way you store a password. If you lose it, or think someone else has it, email [email protected] with the lookup prefix and GridTracker will issue a new key and deactivate the old one. Keys are issued by GridTracker staff and are not tied to a GridTracker web account.

A request with no X-API-Key header gets 401 and MISSING_API_KEY. A key that does not exist and a key that has been deactivated get the same 401 and INVALID_API_KEY, so a response never confirms whether a key exists.

Requests with a missing or invalid key are also counted per calling IP address. After 30 of them in a minute from one address, that address gets 429 with TOO_MANY_FAILED_AUTHENTICATIONS and a Retry-After header, and while the block lasts it applies to requests carrying a valid key too. Requests with a valid key never count toward it, so your own traffic cannot trigger it. A script that retries a bad key in a loop can.

Limits

A key gets these limits when GridTracker issues it:

LimitValue
Requests per minute300
Rows per minute10,000
Default page size100
Maximum page size1,000
Official records inlined on one facility fetch1,000

GridTracker does not adjust page sizes for an individual key. It can raise or lower the requests-per-minute and rows-per-minute limits, so your key may differ from the table: the X-RateLimit-* headers on every response show the limits that apply to it (see Rate limits). A change to a key's limits applies from its next request. The last row is explained under Get one facility.

Interconnection queue projects

An interconnection queue project is a request to connect generation or storage to the transmission grid: one record per queue entry.

EndpointReturns
GET /v2/transmission-grid-interconnection-queue-projectsA page of matching projects
GET /v2/transmission-grid-interconnection-queue-projects/{uniqueId}One project

Only GET is supported. Other methods get 405 with an empty body.

List projects

GET /v2/transmission-grid-interconnection-queue-projects takes these query parameters. Every one is optional.

Paging and sorting

ParameterValueDefaultWhat it does
limitA whole number from 1 to 1,000100The page size.
offsetA whole number, 0 or higher0How many matching projects to skip.
sortByOne of the sort names belowOrdered by uniqueIdThe field to sort on.
sortOrderasc or descascThe direction of the sort.

The sort names:

sortBy valueSorts on
queueIdqueueId, as text
powerMarketpowerMarket, as text
capacityMwcapacityMw, as a number
canonicalGenerationTypesThe generation-type text GridTracker keeps for the project, so a hybrid sorts under its combined name, "Battery + Solar"
statusstatus, as text
projectNameprojectName, as text
proposedCompletionDateproposedCompletionDate
actualCompletionDateactualCompletionDate

Text sorts as text, so P10 comes before P9. Projects with no value for the sort field come last in both directions, so sortBy=capacityMw&sortOrder=desc starts with the largest known capacity. Ties are broken by uniqueId, which keeps paging through a sorted list stable. There is no sort on developers.

Filters

ParameterMatches projects whereExample value
powerMarketspowerMarket equals the valueERCOT
statusesstatus equals the valueActive
generationTypesThe generation-type text equals the value (see below)Battery + Solar
statesAny entry of states equals the valueTX
countiesAny entry of counties equals the value, spelled as it is in the responseAtascosa County, TX
fipsCodesAny entry of fipsCodes equals the value48013
developersThe developer text equals the value (see below)Greenalia
minCapacityMwcapacityMw is at least the value100
maxCapacityMwcapacityMw is at most the value250.5

These are the same filters GridTracker's dashboards use, and they return the same projects for the same inputs.

A project has to satisfy every filter you send. Within one filter, matching any of the values is enough: states=CA&states=TX returns projects in California or Texas, and adding statuses=Active narrows that to the active ones.

Pass several values by repeating the parameter. Never join them with commas. Values are not split, because real values contain commas (Atascosa County, TX). states=CA,TX looks for a state spelled CA,TX and finds nothing.

Some rules for values:

  • Values match exactly, including case. Use the values you see in responses: a project's powerMarket is a valid powerMarkets value, and so on. A filter that matches nothing returns an empty data list and a totalResultCount of 0. It is not an error.
  • Each repeatable filter takes at most 100 values, and each value must be non-empty.
  • generationTypes compares the one text GridTracker keeps for a project's generation types, not the entries of the canonicalGenerationTypes list. A hybrid project is its own value. A project whose canonicalGenerationTypes is ["Battery", "Solar"] matches generationTypes=Battery + Solar, and it does not match generationTypes=Solar or generationTypes=Battery. Hybrids are written with a space, a plus sign and a space between the types.
  • A plus sign in a URL has to be written %2B, because a bare + means a space. curl --data-urlencode does this for you, and the examples below use it.
  • developers compares the project's developer text. For a project with one developer, that is the developer's name. For a project with several, it is one text with the names separated by a semicolon and a space, and only that whole text matches: a single name from the list does not match a multi-developer project.
  • minCapacityMw and maxCapacityMw take decimals such as 2.5. They cannot be negative, and the minimum cannot exceed the maximum. A project with no capacity matches neither bound.

Nothing is adjusted silently. A limit above the maximum page size is rejected, not clamped, and a parameter this endpoint does not know is rejected with INVALID_PARAMETER naming it, so a misspelt filter fails loudly and does not return everything.

Get one project

GET /v2/transmission-grid-interconnection-queue-projects/{uniqueId} returns one project, by the uniqueId field of a list response. If the ID has characters that are not safe in a URL path, such as the * in tristate-generation-transmission-*gi-disis-2024-19, URL-encode them (%2A).

The endpoint takes no query parameters, and one that is sent is rejected. An ID that matches no project gets 404 with NOT_FOUND. A single-project request counts as one request and one row.

The response

A list response has two members:

{
  "data": [{ "queueId": "25INR0082", "...": "..." }],
  "pagination": {
    "limit": 100,
    "offset": 0,
    "totalResultCount": 214,
    "nextOffset": 100
  }
}
  • data is the page: a list of project records, at most limit long.
  • pagination.limit and pagination.offset echo the page size and offset the request used.
  • pagination.totalResultCount is how many projects match your filters, across all pages.
  • pagination.nextOffset is the offset to send for the next page, or null on the last page. It is offset + limit while that is below totalResultCount, so 11 matches with a page size of 10 give a second page.

A single-project response is { "data": { ... } } with the record in data.

Each request reads one consistent snapshot of the data. The pages of a result set are separate requests, though, so if the daily refresh lands between two of them, rows can shift between pages. Walk a full result set in one sitting.

A project record looks like this. The values change as the data does:

{
  "queueId": "25INR0082",
  "projectName": "Reis Solar",
  "uniqueId": "ercot-25inr0082",
  "powerMarket": "ERCOT",
  "iso": "ercot",
  "status": "Active",
  "capacityMw": 105.48,
  "maximumFacilityOutputMw": null,
  "tpdAllocationPercentage": null,
  "tpdAllocationGroup": null,
  "offPeakDeliverability": null,
  "canonicalGenerationTypes": ["Solar"],
  "rawGenerationTypes": "Solar - Photovoltaic Solar",
  "states": ["TX"],
  "counties": ["Atascosa County, TX"],
  "fipsCodes": ["48013"],
  "projectLatitude": 28.652762,
  "projectLongitude": -98.45369,
  "poiLatitude": null,
  "poiLongitude": null,
  "gisLatitude": 28.652762,
  "gisLongitude": -98.45369,
  "gisLatLongSpecificity": "project",
  "interconnectionLocation": "Tapping into 138 kV line (5689) Choke Canyon AEP to (5902) San Miguel Gen",
  "interconnectingEntity": "Greenalia Solar Power Reis LLC",
  "canonicalTransmissionOwners": null,
  "interconnectionVoltageKv": 138,
  "queueDate": "2022-06-02",
  "actualCompletionDate": null,
  "proposedCompletionDate": "2027-08-03",
  "iaDate": null,
  "scheduleNextEventDate": null,
  "scheduleNextEventName": null,
  "scheduleNextDepositDueDate": null,
  "scheduleNextDepositType": null,
  "developers": ["Greenalia"],
  "projectSpv": "Greenalia Solar Power Reis LLC",
  "utility": null,
  "currentPhaseOrStageRaw": "SS Completed, FIS Started, No IA",
  "cluster": null,
  "service": null,
  "iaStatusClean": "Facility Study",
  "mostRecentStudyDate": null,
  "mostRecentStudyUrl": null,
  "mostRecentAllocatedNetworkUpgradeCost": null,
  "projectLinkInGridtracker": "https://www.gridtracker.io/project/ercot-25inr0082"
}

Response fields

Every record has the same 46 fields, in the order below. Field names are camelCase. Values keep their JSON types: lists are arrays, numbers are numbers, and dates are text in YYYY-MM-DD form. A field with no value is null; only uniqueId and projectLinkInGridtracker always have one.

The fields are the columns of GridTracker's dashboard CSV export, under the same names in camelCase, with one rule on top. Where the export offers the same data as plain text and as a list, the API returns only the list. That is why developers, canonicalGenerationTypes and fipsCodes are lists, and why there is no single developer field. The "Dashboard label" column shows what each field is called in the export.

FieldTypeDashboard labelDescription
queueIdtext or nullQueue IDInterconnection queue identifier used by the grid operator
projectNametext or nullProject NameName of the project
uniqueIdtextUnique IDSystem-generated unique identifier, specific to GridTracker
powerMarkettext or nullPower Market"West" or "Southeast" for non-ISO queues. Otherwise, ISO name.
isotext or nullISOIndependent System Operator
statustext or nullStatusCurrent project status (Active, Withdrawn, Operational, Delisted)
capacityMwnumber or nullCapacity (MW)Nameplate capacity in megawatts
maximumFacilityOutputMwnumber or nullMaximum Facility Output (MW)Maximum output capacity (PJM only)
tpdAllocationPercentagenumber or nullTPD Allocation (%)Transmission Plan Deliverability allocation percentage as published by CAISO (CAISO only)
tpdAllocationGrouptext or nullTPD Allocation GroupCAISO TPD allocation group (CAISO only)
offPeakDeliverabilitytext or nullOff-Peak DeliverabilityCAISO off-peak deliverability status (CAISO only)
canonicalGenerationTypeslist of text or nullTechnologyGeneration technology types, mapped to standardized values
rawGenerationTypestext or nullRaw Generation TypesOriginal generation type data, unmapped to standardized values
stateslist of text or nullState(s)State(s) where project is located
countieslist of text or nullCounty/CountiesCounties associated with this project. Usually a single county, but may be multiple for projects which span multiple counties.
fipsCodeslist of text or nullFIPS CodesUS Census-designated area unique identifier
projectLatitudenumber or nullProject LatitudeLatitude of the site of the project
projectLongitudenumber or nullProject LongitudeLongitude of the site of the project
poiLatitudenumber or nullPOI LatitudePoint of Interconnection latitude
poiLongitudenumber or nullPOI LongitudePoint of Interconnection longitude
gisLatitudenumber or nullGIS LatitudeThe most specific latitude GridTracker has for this project, for mapping in a GIS tool: the project site coordinate if there is one, otherwise the point of interconnection, otherwise the centroid of the project's county. gisLatLongSpecificity says which one was used.
gisLongitudenumber or nullGIS LongitudeThe most specific longitude GridTracker has for this project, for mapping in a GIS tool: the project site coordinate if there is one, otherwise the point of interconnection, otherwise the centroid of the project's county. gisLatLongSpecificity says which one was used.
gisLatLongSpecificitytext or nullGIS Lat Long SpecificityHow precise gisLatitude and gisLongitude are: "project" (exact site), "poi" (where the project ties into the grid), or "county" (county centroid only).
interconnectionLocationtext or nullInterconnection LocationPoint of interconnection - often a text description provided by the grid operator
interconnectingEntitytext or nullInterconnecting EntityEntity applying for interconnection. Often same as the project SPV
canonicalTransmissionOwnerstext or nullCanonical Transmission OwnersStandardized transmission owner names
interconnectionVoltageKvnumber or nullInterconnection Voltage (kV)Voltage level of interconnection
queueDatedate or nullQueue DateDate entered queue
actualCompletionDatedate or nullActual Completion DateActual commercial operation date
proposedCompletionDatedate or nullProposed Completion DateProposed commercial operation date
iaDatedate or nullIA DateInterconnection Agreement date
scheduleNextEventDatedate or nullNext Milestone DateNext scheduled event date, such as an upcoming study or decision point window
scheduleNextEventNametext or nullNext Milestone NameName of next scheduled event, such as an upcoming study or decision point window
scheduleNextDepositDueDatedate or nullNext Deposit Due DateDate when the next deposit is due
scheduleNextDepositTypetext or nullNext Deposit TypeType of the next deposit required
developerslist of text or nullDevelopers (JSON)All developers of the project, as a list.
projectSpvtext or nullProject SPVSpecial Purpose Vehicle
utilitytext or nullUtilityUtility company
currentPhaseOrStageRawtext or nullCurrent Phase/StageCurrent project phase or stage, exactly as it is described by the grid operator, not standardized across grid operators
clustertext or nullClusterCluster this project is a part of, if any
servicetext or nullServiceType of service requested (NRIS vs ERIS)
iaStatusCleantext or nullIA StatusStandardized mapping of project's status in the process to achieving a signed interconnection agreement (IA)
mostRecentStudyDatedate or nullMost Recent Study DateDate of most recent study
mostRecentStudyUrltext or nullMost Recent Study URLLink to most recent study
mostRecentAllocatedNetworkUpgradeCostnumber or nullNetwork Upgrade CostMost recent allocated network upgrade cost
projectLinkInGridtrackerURLLink to project in GridTrackerURL to view this project in GridTracker

Data center facilities

A data center facility is one building: a data center GridTracker has identified from the official records filed about it and from directory listings and news reports, with a name, the companies behind it, an address, a status and a power capacity resolved across every one of those sources. A facility known only from listings is served too, with officialRecordCount: 0 and a capacity that comes from the listings. The API serves the 12,999 building-level facilities in the current snapshot. Many of them sit on a campus. A facility names its campus in campusId and campusName, and the campusIds filter lists the facilities of one campus, but the campus is not an item of this entity: the snapshot's 49 campus-level entries, whose capacity totals their buildings, are left out of the list, the single fetch and the aggregate, so no capacity is ever counted twice.

facilityId is 12 characters long and does not change when the snapshot refreshes. It is the id the GridTracker web app and the MCP use for the same facility, and facilityLinkInGridtracker is the facility's page in the web app.

Every response from a data center endpoint carries the header X-GridTracker-Data-Center-Data-Version (see The data center snapshot).

EndpointReturns
GET /v2/data-center-facilitiesA page of matching facilities
GET /v2/data-center-facilities/{facilityId}One facility, with its official records
GET /v2/data-center-facilities/aggregateCounts and capacity metrics over matching facilities, grouped (see Aggregating facilities)

Only GET is supported. Other methods get 405 with an empty body.

List facilities

GET /v2/data-center-facilities takes limit, offset, sortBy and sortOrder exactly as the queue projects list does (see Paging and sorting), with the sort names below, and the filters below. Every parameter is optional, and the rules for values are the queue projects' rules: a facility has to satisfy every filter you send; within one filter, matching any of the values is enough; several values are given by repeating the parameter, never by joining them with commas; each repeatable filter takes at most 100 values, each non-empty; values match exactly, including case; and a parameter the endpoint does not know is rejected with INVALID_PARAMETER naming it.

Facility sorts

sortBy valueSorts on
facilityNamefacilityName, as text
statestate, as text
facilityStatusfacilityStatus, as text, so alphabetically (Cancelled before Construction) rather than by stage
powerCapacityMwpowerCapacityMw, as a number
firstRecordDatefirstRecordDate
lastRecordDatelastRecordDate
proposedOperationalDateproposedOperationalDate
facilityIdfacilityId, the order you get with no sortBy
distanceMilesDistance from the point of the proximity filter, nearest first with asc. Valid only when the filter is present (see below)

Facilities with no value for the sort field come last in both directions, and ties are broken by facilityId, so paging through a sorted list is stable.

Facility filters

ParameterMatches facilities whereExample value
statesstate equals the valueVA
countiescounty equals the value, spelled as it is in the responseLoudoun
citiescity equals the valueAshburn
facilityStatusesfacilityStatus equals the valueConstruction
gridOperatorsgridOperator equals the valueDominion Energy Virginia
isHyperscalerisHyperscaler equals the value. One value, not repeatableYes
facilityCommercialTypesfacilityCommercialType equals the valuecommercial
facilityTagsAny entry of facilityTags equals the valuehyperscaler
campusIdscampusId equals the valuecorscale-gainesville-crossing-va
campusNameContainscampusName contains the text, ignoring case. One valueGainesville
facilityGroupingConfidenceTiersfacilityGroupingConfidenceTier equals the value. Matches nothing until the tier is populatedconfident
developersdeveloper equals the valueCorscale
operatorsoperator equals the valueEquinix
ownersowner equals the valueDigital Realty
companyNameContainsdeveloper, operator or owner contains the text, ignoring case. One valueQTS
facilityNameContainsfacilityName contains the text, ignoring case. One valueBuilding 1
sourceCategoriesAny entry of sourceCategories equals the valuegovernment_permit
permitTypesAny of the facility's served official records has a permitType equal to the valueAir Quality Permit
permitStatusesAny of the facility's served official records has a permitStatus equal to the valueIssued
coolingTypesAny of the facility's served official records has a coolingType equal to the valueAir-cooled
hasOfficialRecordstrue: officialRecordCount is above zero. false: it is zerotrue
minPowerCapacityMwpowerCapacityMw is at least the value100
maxPowerCapacityMwpowerCapacityMw is at most the value500
firstRecordDateFromfirstRecordDate is on or after the value2024-01-01
firstRecordDateTofirstRecordDate is on or before the value2024-12-31
lastRecordDateFromlastRecordDate is on or after the value2025-01-01
lastRecordDateTolastRecordDate is on or before the value2025-12-31
proposedOperationalDateFromproposedOperationalDate is on or after the value2027-01-01
proposedOperationalDateToproposedOperationalDate is on or before the value2027-12-31
latitudeThe facility's coordinates are within radiusMiles of this point. Needs longitude and radiusMiles too45.54
longitudeThe longitude of that point-122.95
radiusMilesThe radius around that point, in miles: above 0 and at most 5005

Some rules for values:

  • The filters on a field with a fixed list of values (facilityStatuses, isHyperscaler, facilityCommercialTypes, facilityTags, facilityGroupingConfidenceTiers, sourceCategories) accept only the values listed for that field in Facility fields. Anything else is INVALID_PARAMETER, and the message lists the allowed values.
  • The other text filters compare exactly, so use spellings from responses: a facility's gridOperator is a valid gridOperators value. A value that matches nothing returns an empty data list and a totalResultCount of 0, which is not an error.
  • campusNameContains, companyNameContains and facilityNameContains take one value and match it anywhere in the field, ignoring case.
  • hasOfficialRecords is exactly true or false.
  • minPowerCapacityMw and maxPowerCapacityMw take non-negative decimals. The minimum cannot exceed the maximum, and a facility with no capacity matches neither bound.
  • Date bounds are YYYY-MM-DD, inclusive, and must be real dates. A From cannot be later than its To, and a facility with no such date matches neither bound.

The proximity filter is latitude (-90 to 90), longitude (-180 to 180) and radiusMiles (above 0 and at most 500), sent together. Sending one or two of them is INVALID_PARAMETER naming the missing one. When the filter is present, every facility in the response carries distanceMiles, the great-circle distance in miles from your point to the facility's coordinates, to two decimals, and sortBy=distanceMiles becomes valid. A facility without coordinates is never within any radius, and a facility placed at county precision is measured from the county's centre, so check latLongPrecision before trusting a small distance.

Get one facility

GET /v2/data-center-facilities/{facilityId} returns one facility by its facilityId, together with its official records: { "data": { ...the facility fields..., "officialRecords": [...], "officialRecordsTruncated": false } }. The endpoint takes no query parameters, and one that is sent is rejected.

officialRecords holds the facility's served records in application-date order, undated records last, up to 1,000 of them. A key whose rows-per-minute limit is below 1,001 gets a smaller cap: that limit minus one. When the facility has more served records than the cap, officialRecordsTruncated is true, and the official records endpoint with facilityIds serves all of them in pages (see One facility with its official records). The largest facility in the current snapshot has 316 served records, so a key at the limits above sees no truncation today.

A facility fetch costs one request and one row for the facility plus one row per inlined record, and the whole cost is checked before any of it is charged: with 7 rows left in the minute, a facility with 10 records is refused with ROW_LIMIT_EXCEEDED and nothing is charged. The API never serves a facility with fewer records to make it fit.

An id that matches no building-level facility gets 404 with NOT_FOUND. That includes the id of a campus-level entry, which you can meet in an official record's facilityId (see Data center official records).

Facility fields

Every facility has the same 28 fields, in the order below, plus three that depend on the request: distanceMiles is present only when the proximity filter is used, and officialRecords and officialRecordsTruncated only on the single-facility fetch. Field names are camelCase, lists are arrays, numbers are numbers, and dates are text in YYYY-MM-DD form. A field with no value is null.

A facility looks like this, from a list response. The values change as the data does:

{
  "facilityId": "10cd3fc5fc50",
  "facilityName": "Gainesville Crossing Building 1",
  "developer": "Corscale",
  "operator": "Corscale",
  "owner": null,
  "address": "13760 University Blvd, Gainesville, VA",
  "city": "Gainesville",
  "county": "Prince William",
  "state": "VA",
  "facilityStatus": "Operational",
  "powerCapacityMw": 100.8,
  "gridOperator": "Dominion Energy Virginia",
  "facilityCommercialType": "commercial",
  "facilityTags": ["wholesale"],
  "isHyperscaler": "No",
  "campusId": "corscale-gainesville-crossing-va",
  "campusName": "Corscale Gainesville Crossing Data Campus (Gainesville, VA)",
  "latitude": 38.80251062876824,
  "longitude": -77.5823887783089,
  "latLongPrecision": "direct",
  "facilityGroupingConfidenceTier": null,
  "officialRecordCount": 10,
  "sourceCategories": ["government_permit", "industry_report"],
  "firstRecordDate": "2021-05-21",
  "lastRecordDate": "2024-08-22",
  "proposedOperationalDate": null,
  "actualOperationalDate": "2024-05-24",
  "facilityLinkInGridtracker": "https://www.gridtracker.io/datacenter/facility/10cd3fc5fc50"
}
FieldTypeLabelDescription
facilityIdtextFacility IDGridTracker's 12-character id for the facility. It stays the same across snapshot refreshes, and it is the id the GridTracker web app and the MCP use for the same facility.
facilityNametext or nullFacility nameThe facility's name: the best-known name across every source the snapshot holds for the site, its official records and other evidence such as directory listings and news reports alike.
developertext or nullDeveloperThe company developing the facility, taken as the most common developer named across every source the snapshot holds for the site, official records and other evidence alike.
operatortext or nullOperatorThe company operating the facility, taken as the most common operator named across every source the snapshot holds for the site, official records and other evidence alike.
ownertext or nullOwnerThe company that owns the facility, taken as the most common owner named across every source the snapshot holds for the site, official records and other evidence alike.
addresstext or nullAddressStreet address of the site, from the source record GridTracker treats as the facility's primary one, whatever kind of source it is.
citytext or nullCityCity of the site, from the same primary source record.
countytext or nullCountyCounty of the site, from the same primary source record, spelled as that source spells it.
statetext or nullStateTwo-letter state or province code of the site.
facilityStatustextStatusThe facility's stage, resolved across every source the snapshot holds for the site with the most advanced one winning: Operational over Construction over Proposed. One of Proposed, Construction, Operational, Decommissioned, Cancelled, Withdrawn, Unknown.
powerCapacityMwnumber or nullPower capacity (MW)The upstream facility-level capacity in megawatts: the largest value recorded for the site across every underlying record, directory listings and news reports included, so a facility with no official record can still carry one. When the figure comes from an air permit it is often generator capacity rather than load. Treat it as an upper bound.
gridOperatortext or nullGrid operatorThe grid operator or utility whose territory the facility sits in, as its records state it.
facilityCommercialTypetextCommercial typeWhether the facility sells capacity to others (commercial) or serves its owner's own operations (internal-corporate). One of commercial, internal-corporate, unknown.
facilityTagslist of text or nullFacility tagsWhat kind of data center it is, as a list; a facility can carry several tags. Null when none is known. Each entry is one of colocation, hyperscaler, neocloud, wholesale, crypto-mining, telecom, edge, government.
isHyperscalertextHyperscalerWhether a hyperscale operator is behind the facility, taking the strongest answer across every source the snapshot holds for the site: Yes over Likely over No over Unknown. One of Yes, Likely, No, Unknown.
campusIdtext or nullCampus IDThe id of the campus the facility belongs to, or null when it stands alone. Campuses are not served as their own entity; the campusIds filter lists a campus's facilities.
campusNametext or nullCampus nameThe name of that campus, or null.
latitudenumber or nullLatitudeLatitude of the site in decimal degrees, from the most precise coordinates among every source the snapshot holds for the site. Null when no source places the facility.
longitudenumber or nullLongitudeLongitude of the site in decimal degrees, from the same source as latitude. Null when no source places the facility.
latLongPrecisiontext or nullCoordinate precisionHow precisely the coordinates place the facility. direct is the site itself, address its street address, and zip, city and county the centre of that area; approximate and derived are rougher placements. Null when there are no coordinates. One of direct, address, zip, city, county, approximate, derived.
facilityGroupingConfidenceTiertext or nullGrouping confidenceHow confident GridTracker is that the records grouped under this facility belong together. Not populated yet: null on every facility until the pipeline exports it, and the facilityGroupingConfidenceTiers filter matches nothing until then. One of confident, potential.
officialRecordCountwhole numberOfficial record countHow many official records the API serves for this facility. Zero for a facility whose only evidence is directory listings, which the API does not serve; the facility is still served with its name, companies, address, status and capacity.
sourceCategorieslist of textSource categoriesThe kinds of source the facility's records come from, counting records the API does not serve. A facility known only from directory listings or news coverage reports industry_report, so you can tell it has that evidence without seeing it. sourceCategory on official records says what the other categories cover. Each entry is one of government_permit, utility_data, environmental_org, research_dataset, industry_report.
firstRecordDatedate or nullFirst record dateThe earliest date on any of the facility's served records, across their application, approval, proposed operational and actual operational dates.
lastRecordDatedate or nullLast record dateThe latest of those same dates. A facility whose last record date is years old has filed nothing new that GridTracker has seen.
proposedOperationalDatedate or nullProposed operational dateThe latest proposed operational date across the facility's official records. Records can disagree, and the field holds the furthest-out date, whichever record filed it.
actualOperationalDatedate or nullActual operational dateThe earliest actual operational date across the facility's served records.
distanceMilesnumber, not on every responseDistance (miles)Distance in miles from the point in your proximity filter, to two decimals. Present only when the request has latitude, longitude and radiusMiles.
officialRecordslist of official records, not on every responseOfficial recordsThe facility's official records, in application-date order with undated records last, up to the inline cap for your key. Present only on the single-facility fetch.
officialRecordsTruncatedboolean, not on every responseOfficial records truncatedTrue when the facility has more served records than were inlined; the official records endpoint with facilityIds serves the rest. Present only on the single-facility fetch.
facilityLinkInGridtrackerURLLink to facility in GridTrackerURL of the facility's page in GridTracker.

Data center official records

An official record is one document about a data center from a source that is not a commercial directory: a government permit (air, building, grading, site plan, zoning), a planning or land record, a tax record, a regulatory filing, a utility or public utility commission docket, a load-queue entry, an environmental organisation's record, or an entry in a public research dataset with a published methodology. Third-party directory listings and licensed press are never served. Where a facility is known only from them, the facility itself is still served (see The data center snapshot), and its sourceCategories says that listing evidence exists.

The current snapshot has 11,122 official records: 10,395 government permits and filings, 449 environmental organisation records, 209 utility records and 69 research dataset records (sourceCategory: "research_dataset"). 460 of them belong to no facility, because GridTracker could not match them to a building; they are served with facilityId: null. Another 336 belong to a campus-level entry rather than to a building: their facilityId is that entry's id, which the facilities endpoint answers with 404, and their campusId names the campus, whose buildings the facilities endpoint lists under campusIds. The API never reassigns a record to a building.

recordId is 12 characters long and does not change when the snapshot refreshes. It is the id the GridTracker web app's record pages and the MCP use, and recordLinkInGridtracker is the record's page in the web app.

A record stays in the dataset after its source stops listing it: isDelistedBySource is then true and sourceLastSeenDate says when GridTracker last saw it there. 876 records are delisted today. Send isDelistedBySource=false to see only what sources still show.

permitUrl is the source's own page for the record. It is null when that page is on a third-party directory, which the API never links to; 12 records are affected today.

EndpointReturns
GET /v2/data-center-official-recordsA page of matching records
GET /v2/data-center-official-records/{recordId}One record

Only GET is supported. Other methods get 405 with an empty body.

List official records

GET /v2/data-center-official-records takes limit, offset, sortBy and sortOrder as every list does, with the sort names below, and the filters below. Every parameter is optional, and the rules for values are the facility filters' rules.

Official record sorts

sortBy valueSorts on
applicationDateapplicationDate
approvalDateapprovalDate
proposedOperationalDateproposedOperationalDate
actualOperationalDateactualOperationalDate
powerCapacityMwpowerCapacityMw, as a number
statestate, as text
dataCenterStatusdataCenterStatus, as text
recordIdrecordId, the order you get with no sortBy
distanceMilesDistance from the point of the proximity filter. Valid only when the filter is present

Records with no value for the sort field come last in both directions, and ties are broken by recordId.

Official record filters

ParameterMatches records whereExample value
facilityIdsfacilityId equals the value10cd3fc5fc50
campusIdscampusId equals the valueqts-atlanta-1-ga
statesstate equals the valueGA
countiescounty equals the value, spelled as it is in the responseFulton
citiescity equals the valueAtlanta
dataCenterStatusesdataCenterStatus equals the valueProposed
sourceCategoriessourceCategory equals the valueutility_data
permitTypespermitType equals the valueAir Quality Permit
permitStatusespermitStatus equals the valueIssued
coolingTypescoolingType equals the valueAir-cooled
isHyperscalerisHyperscaler equals the value. One value, not repeatableYes
isDelistedBySourceisDelistedBySource equals the valuefalse
developersdeveloper equals the valueCorscale
operatorsoperator equals the valueEquinix
ownersowner equals the valueDigital Realty
companyNameContainsdeveloper, operator or owner contains the text, ignoring case. One valueQTS
hasFacilitytrue: facilityId is set, a campus-level entry's id included. false: it is nullfalse
minPowerCapacityMwpowerCapacityMw is at least the value100
maxPowerCapacityMwpowerCapacityMw is at most the value500
applicationDateFromapplicationDate is on or after the value2025-07-01
applicationDateToapplicationDate is on or before the value2025-09-30
approvalDateFromapprovalDate is on or after the value2025-01-01
approvalDateToapprovalDate is on or before the value2025-12-31
proposedOperationalDateFromproposedOperationalDate is on or after the value2027-01-01
proposedOperationalDateToproposedOperationalDate is on or before the value2027-12-31
actualOperationalDateFromactualOperationalDate is on or after the value2024-01-01
actualOperationalDateToactualOperationalDate is on or before the value2024-12-31
latitudeThe record's coordinates are within radiusMiles of this point. Needs longitude and radiusMiles too33.76
longitudeThe longitude of that point-84.42
radiusMilesThe radius around that point, in miles: above 0 and at most 50010

The proximity filter works as it does for facilities: all three parameters or none, distanceMiles on every record in the response, sortBy=distanceMiles valid only then, and a record without coordinates never within any radius.

Get one official record

GET /v2/data-center-official-records/{recordId} returns one record by its recordId. It takes no query parameters, an id that matches no record gets 404 with NOT_FOUND, and the request counts as one request and one row.

Official record fields

Every record has the same 42 fields, in the order below, plus distanceMiles when the proximity filter is used. Field names are camelCase, numbers are numbers, and dates are text in YYYY-MM-DD form. A field with no value is null; only recordId, dataCenterStatus, sourceCategory, isDelistedBySource and recordLinkInGridtracker always have one.

A record looks like this. The values change as the data does:

{
  "recordId": "008a50835c3c",
  "facilityId": "b5af76544504",
  "campusId": "qts-atlanta-1-ga",
  "developmentName": "QTS METRO- NORTH ELEVATOR UPGRADE",
  "operationalName": null,
  "developer": null,
  "operator": null,
  "owner": null,
  "projectSpv": null,
  "address": null,
  "city": "Atlanta",
  "county": "Fulton",
  "state": "GA",
  "zipCode": null,
  "fipsCode": "13121",
  "latitude": 33.762909,
  "longitude": -84.422675,
  "latLongPrecision": "city",
  "dataCenterStatus": "Unknown",
  "sourceDataCenterStatus": "Commercial QCR: Create Application",
  "sourceCategory": "government_permit",
  "permitId": "QCR-2019-06309",
  "permitType": "Commercial QCR",
  "permitStatus": "Create Application",
  "permitUrl": "https://aca-prod.accela.com/ATLANTA_GA/Cap/CapHome.aspx?module=Building",
  "applicationDate": "2019-10-21",
  "approvalDate": null,
  "proposedOperationalDate": null,
  "actualOperationalDate": null,
  "powerCapacityMw": null,
  "itLoadMw": null,
  "floorAreaSqFt": null,
  "powerUsageEffectiveness": null,
  "waterUsageGallonsPerDay": null,
  "coolingType": null,
  "totalCapitalCost": null,
  "isHyperscaler": "No",
  "gridOperator": null,
  "derivedGridOperator": "GEORGIA POWER CO",
  "isDelistedBySource": false,
  "sourceLastSeenDate": null,
  "recordLinkInGridtracker": "https://www.gridtracker.io/datacenter/permit/008a50835c3c"
}
FieldTypeLabelDescription
recordIdtextRecord IDGridTracker's 12-character id for the record. It stays the same across snapshot refreshes, and it is the id the GridTracker web app's record pages and the MCP use for the same record.
facilityIdtext or nullFacility IDThe id of the facility this record belongs to, or null when GridTracker has not matched it to one. Some records belong to a campus-level entry rather than a building; that id answers 404 on the facilities endpoint, and campusId names the campus.
campusIdtext or nullCampus IDThe campus of the owning facility, or null.
developmentNametext or nullDevelopment nameThe name the project was filed under while it was being developed.
operationalNametext or nullOperational nameThe name the data center operates under, when the record gives one.
developertext or nullDeveloperThe developer named on the record.
operatortext or nullOperatorThe operator named on the record.
ownertext or nullOwnerThe owner named on the record.
projectSpvtext or nullProject SPVThe special purpose vehicle or project company named on the record, often the applicant on a permit.
addresstext or nullAddressStreet address on the record.
citytext or nullCityCity on the record.
countytext or nullCountyCounty on the record, spelled as the source spells it.
statetext or nullStateTwo-letter state or province code on the record.
zipCodetext or nullZIP codePostal code on the record.
fipsCodetext or nullFIPS codeThe US Census county code of the site, for joining to county-level data.
latitudenumber or nullLatitudeLatitude of the site in decimal degrees, as this record places it.
longitudenumber or nullLongitudeLongitude of the site in decimal degrees, as this record places it.
latLongPrecisiontext or nullCoordinate precisionHow precisely the coordinates place the site. direct is the site itself, address its street address, and zip, city and county the centre of that area; approximate and derived are rougher placements. Null when there are no coordinates. One of direct, address, zip, city, county, approximate, derived.
dataCenterStatustextData center statusThe data center's stage as this record states it, normalised to GridTracker's statuses. Records of one facility can disagree; the facility's facilityStatus resolves them. One of Proposed, Construction, Operational, Decommissioned, Cancelled, Withdrawn, Unknown.
sourceDataCenterStatustext or nullSource status wordingThe source's own wording of that status, before normalisation.
sourceCategorytextSource categoryWhat kind of source the record comes from. government_permit covers permits, planning, land and tax records and regulatory filings; utility_data covers utility and grid operator dockets and load queues; environmental_org covers environmental organisations' records; research_dataset covers public research datasets with a published methodology. industry_report is reserved: directory listings and news coverage are not official records, so no served record carries it today. One of government_permit, utility_data, environmental_org, research_dataset, industry_report.
permitIdtext or nullPermit IDThe source's own identifier for the record, such as a permit or docket number.
permitTypetext or nullPermit typeThe kind of record, as the source names it: an air permit, a site plan, a rezoning case, a utility docket and so on.
permitStatustext or nullPermit statusThe record's own status in the source's words. Not normalised, so the same stage reads differently from source to source.
permitUrltext or nullPermit URLThe source's own page for the record. Null when that page is on a third-party directory, which the API never links to.
applicationDatedate or nullApplication dateWhen the application or filing was made.
approvalDatedate or nullApproval dateWhen the source approved or issued it.
proposedOperationalDatedate or nullProposed operational dateWhen the record says the data center is planned to go live.
actualOperationalDatedate or nullActual operational dateWhen the record says the data center went live.
powerCapacityMwnumber or nullPower capacity (MW)Power capacity in megawatts as the record states it. On an air permit this is often the generator nameplate, an upper bound on the facility's load.
itLoadMwnumber or nullIT load (MW)IT load in megawatts, where the record states one.
floorAreaSqFtnumber or nullFloor area (sq ft)Floor area in square feet, where the record states one.
powerUsageEffectivenessnumber or nullPower usage effectivenessThe PUE the record states, where it states one.
waterUsageGallonsPerDaynumber or nullWater usage (gallons per day)Water consumption in gallons per day, where the record states one.
coolingTypetext or nullCooling typeThe cooling method, in the source's words.
totalCapitalCosttext or nullTotal capital costThe capital cost as the record gives it, kept as text, for example $1,800,000,000.
isHyperscalertext or nullHyperscalerWhether this record points to a hyperscale operator. Null when the record gives no indication. One of Yes, Likely, No, Unknown.
gridOperatortext or nullGrid operatorThe grid operator or utility as the record states it.
derivedGridOperatortext or nullDerived grid operatorThe grid operator GridTracker infers from the site's location and the utility territory it falls in. Useful when the record itself names none.
isDelistedBySourcebooleanDelisted by sourceTrue when the source no longer lists this record, which happens when a permit expires or a docket closes. Read it as a note on the source, and look to the facility's other records for the project.
sourceLastSeenDatedate or nullSource last seen dateThe last date GridTracker saw the record at its source. Set for delisted records.
distanceMilesnumber, not on every responseDistance (miles)Distance in miles from the point in your proximity filter, to two decimals. Present only when the request has latitude, longitude and radiusMiles.
recordLinkInGridtrackerURLLink to record in GridTrackerURL of the record's page in GridTracker.

Aggregating facilities

GET /v2/data-center-facilities/aggregate groups the facilities that match your filters and returns one row per group, with the metrics you ask for. It takes every filter of List facilities, the proximity filter included, plus these parameters:

ParameterValueDefaultWhat it does
groupByOne of the group-by names below. RequiredThe field groups are formed on
metricsOne or more of the metric names below, by repeating the parameter. RequiredThe numbers computed for each group
sortByOne of the metrics you asked forThe first metric asked forThe metric groups are ordered by
sortOrderasc or descdescLargest groups first unless you say otherwise
limitA whole number from 1 to 1,000100Groups per page
offsetA whole number, 0 or higher0Groups to skip

The group-by names:

groupBy valueGroups facilities by
statestate
countycounty
facilityStatusfacilityStatus
gridOperatorgridOperator
developerdeveloper
operatoroperator
ownerowner
isHyperscalerisHyperscaler
campusIdcampusId
facilityCommercialTypefacilityCommercialType
facilityGroupingConfidenceTierfacilityGroupingConfidenceTier, one null group until the tier is populated
coolingTypeThe cooling type of the first served official record that states one
firstRecordYearThe year of firstRecordDate, as 2024
firstRecordQuarterThe quarter of firstRecordDate, as 2024-Q3
firstRecordMonthThe month of firstRecordDate, as 2024-09
lastRecordYearThe year of lastRecordDate
lastRecordQuarterThe quarter of lastRecordDate
lastRecordMonthThe month of lastRecordDate

The metrics:

metrics valueComputes
facilityCountHow many facilities are in the group
sumPowerCapacityMwThe sum of powerCapacityMw over the facilities that have one
avgPowerCapacityMwTheir average
minPowerCapacityMwThe smallest
maxPowerCapacityMwThe largest
sumItLoadMwThe sum of IT load, each facility's taken from the first served official record that states one
sumFloorAreaSqFtThe sum of floor area, taken the same way

The response has three members:

{
  "data": [
    {
      "groupingValues": { "state": "VA" },
      "facilityCount": 2329,
      "sumPowerCapacityMw": 102496.449
    },
    { "...": "..." }
  ],
  "aggregation": {
    "groupBy": "state",
    "metrics": ["facilityCount", "sumPowerCapacityMw"],
    "totalFacilitiesConsidered": 12999,
    "totalGroupCount": 54
  },
  "pagination": {
    "limit": 5,
    "offset": 0,
    "totalResultCount": 54,
    "nextOffset": 5
  }
}
  • data is the page of groups. Each group carries the key it was formed on under groupingValues, and one member per requested metric.
  • aggregation echoes groupBy and metrics, and says how many facilities your filters admitted (totalFacilitiesConsidered) and how many groups they fell into (totalGroupCount).
  • pagination counts groups: totalResultCount equals totalGroupCount, and nextOffset pages through the groups exactly as it pages through items.

Facilities with no value for the group-by field form one group whose key is null, returned like any other, so the facility counts of all the groups add up to totalFacilitiesConsidered. Grouped by firstRecordYear, that group is the largest, because most facilities have no dated official record. A metric over no values is null: a group where no facility states a capacity has sumPowerCapacityMw: null, never 0. Sums and averages are rounded to three decimals.

Each group returned counts as one row. Like a list page, an aggregate request is refused outright when fewer than limit rows are left, reserves limit rows before it runs, and gives back the rows it does not use, so the cost is the groups you receive. Campus-level entries are not in the population at all, so a campus's capacity enters a sum once, through its buildings.

The data center snapshot

The data center endpoints answer from one snapshot of GridTracker's data center dataset, the same one the GridTracker web app and the MCP read. It refreshes on an irregular schedule, whenever the data pipeline publishes a new one: several times a month lately, on no fixed day. Every response from a data center endpoint, errors and refusals included, carries the header X-GridTracker-Data-Center-Data-Version with the snapshot's version: the date of the upstream data followed by a hash of the file, such as 2026-10-01-ac2cac2f6836. Keep the last value you saw and compare. A change means a refresh has landed, and that is the moment to pull again; while the value stays the same, the data is the same, however far apart your requests are. The snapshot version is not an API version, and the rules in Versions are untouched by a refresh.

Ids persist across refreshes: a facilityId or recordId you have stored keeps pointing at the same facility or record. What can change under an id is its content, as new records arrive and the facility's resolved name, companies, status, capacity and dates follow them. The counts in this guide are from snapshot 2026-10-01-ac2cac2f6836 and move with each refresh.

Some limits of the data, stated plainly:

  • Capacity is an upper bound. A facility's powerCapacityMw is the largest capacity recorded for the site across every underlying record, directory listings and news reports included, so a facility with no official record can still carry one, from a listing's estimate. Where the figure does come from an air permit, it is often the generator nameplate rather than the load the facility draws. Treat the value as a ceiling on what has been claimed for the site, and look at the facility's official records to see what stands behind it.
  • Absence of a record does not mean absence of a permit. GridTracker reads a set of permitting systems, and a permit filed somewhere it does not read leaves no record. officialRecordCount: 0 means no record in GridTracker and nothing more. Most facilities are in that position: 10,281 of the 12,999 are known from directory listings only, and are served with their resolved fields, officialRecordCount: 0 and sourceCategories: ["industry_report"]. Send hasOfficialRecords=true to see only the facilities with a document behind them.
  • Grouping records into facilities is heuristic. Records are matched by coordinates, address and company, and the result can split one site into two facilities or merge two neighbours into one. facilityGroupingConfidenceTier is where GridTracker will state its confidence once the pipeline exports it; until then the field is null.
  • Coverage is the United States and Canada.
  • Coordinates: 11,136 of the 12,999 facilities have them. 9,679 are placed at the site itself or its street address, 153 at a ZIP code or city, 1,304 at the county or an approximate point, and 1,863 have none. latLongPrecision says which, and the proximity filter skips the facilities without coordinates.

Rate limits and their headers

Each key has two limits, both counted over a sliding one-minute window and shared across every endpoint and every version:

  • Requests per minute. Every request made with a valid key counts as one request, whatever its outcome: a page of results, a project that does not exist, a rejected parameter and a server error all cost one.
  • Rows per minute. A row is one item returned: a queue project, a data center facility, an official record, each official record inlined on a facility fetch, and each group of an aggregate. A request is accepted only if the key has at least as many rows left as it could return: limit on a list or an aggregate, the default page size when you send none, one for a single project or record, and one plus the number of inlined records for a single-facility fetch (see Get one facility). It is then charged for the rows it actually receives. A request that returns nothing, because the page is past the end or the parameters were invalid, costs no rows.

A request refused for exceeding either limit counts against neither. When your key has 70 rows left and you ask for the default page of 100, you are refused even if only 30 items would have matched. Ask for limit=70 instead, or wait.

Capacity comes back gradually, as earlier usage ages out of the window, so a request that waits out Retry-After can still be refused if it asks for more than has come back. The refusal carries a new Retry-After.

Every response to a request made with a valid key, success or error, carries these headers:

HeaderValue
X-RateLimit-Requests-LimitRequests per minute your key is allowed
X-RateLimit-Requests-RemainingRequests left in the window
X-RateLimit-Requests-ResetThe end of the current one-minute window, as a Unix timestamp in seconds
X-RateLimit-Rows-LimitRows per minute your key is allowed
X-RateLimit-Rows-RemainingRows left in the window, which is the largest limit the next request can ask for
X-RateLimit-Rows-ResetThe end of the current one-minute window for rows, as a Unix timestamp in seconds
Retry-AfterOn 429 responses only: the number of seconds to wait before trying again, from the counter that refused you

The headers are final when you receive them. A request that asked for 1,000 rows and got 30 reports 9,970 remaining. The two windows need not end at the same moment, which is why each counter has its own reset.

When GridTracker cannot check a limit, the request is allowed through and the limit headers are left off the response. Limits protect the service against runaway clients, so an outage of the limit store does not stop requests from being served. Responses to a missing or invalid key carry no limit headers, and a TOO_MANY_FAILED_AUTHENTICATIONS response carries only Retry-After.

To read the headers, ask curl to print them and drop the body:

curl -s -D - -o /dev/null \
  "https://api.gridtracker.io/v2/transmission-grid-interconnection-queue-projects?limit=1" \
  -H "X-API-Key: gtapi_<your key>"

The rate-limit lines of a response look like this:

x-ratelimit-requests-limit: 300
x-ratelimit-requests-remaining: 299
x-ratelimit-requests-reset: 1791303960
x-ratelimit-rows-limit: 10000
x-ratelimit-rows-remaining: 9999
x-ratelimit-rows-reset: 1791303960

HTTP header names are case-insensitive, so your client may show them in any case.

Errors

Every error uses one envelope:

{
  "error": {
    "code": "INVALID_PARAMETER",
    "message": "Invalid value for parameter \"limit\": must be a whole number from 1 to 1000 on this key's plan",
    "parameter": "limit"
  }
}

Branch on code, which never changes meaning. message is written for people, and its wording can change. parameter appears only on INVALID_PARAMETER, and names the query parameter at fault (or id for a bad ID in the path).

CodeStatusMeaning
MISSING_API_KEY401The request has no X-API-Key header.
INVALID_API_KEY401The key does not exist or has been deactivated. The two cases look alike on purpose.
TOO_MANY_FAILED_AUTHENTICATIONS429The calling address sent too many requests with a missing or invalid key in the last minute. Fix the key, then wait for Retry-After.
INVALID_PARAMETER400A parameter is unknown, repeated when it may be given once, or out of range, and parameter names it. On the data center endpoints that includes a proximity filter with a parameter missing, sortBy=distanceMiles without the filter, and an aggregate with an unknown groupBy or metric. Nothing is clamped or corrected. Fix the request.
NOT_FOUND404No item has that id (on the facilities endpoint, a campus-level entry's id included), or no endpoint exists at that path.
API_VERSION_RETIRED410The path is under a retired version, today every /v1 path.
REQUEST_LIMIT_EXCEEDED429Your key has used all of its requests for the current minute. Wait for Retry-After.
ROW_LIMIT_EXCEEDED429Your key does not have enough rows left for the page size you asked for. Ask for a page no larger than X-RateLimit-Rows-Remaining, or wait for Retry-After.
SERVICE_UNAVAILABLE503The key store, the data store or the data center snapshot is temporarily unavailable. Retry in a few seconds. No rows are charged.
INTERNAL_ERROR500The server could not build a valid response for this request. The failure is logged on GridTracker's side. Retry once, and if it repeats, email support with the time and the URL.

A path the API does not serve, such as /dashboards or a misspelt endpoint, returns 404 with the same envelope and the code NOT_FOUND, never an HTML page. Paths under /v1 return 410.

Every response carries Cache-Control: no-store. The API sends no CORS headers, so a browser blocks calls to it from a web page on another origin: call it from a server or a script.

Worked examples

Each example needs only your key. -G with --data-urlencode makes curl build and encode the query string, so a value such as Battery + Solar is safe to type as it reads.

A filtered list

Active solar projects of at least 100 MW in Texas, two to a page:

curl -s -G "https://api.gridtracker.io/v2/transmission-grid-interconnection-queue-projects" \
  -H "X-API-Key: gtapi_<your key>" \
  --data-urlencode "states=TX" \
  --data-urlencode "statuses=Active" \
  --data-urlencode "generationTypes=Solar" \
  --data-urlencode "minCapacityMw=100" \
  --data-urlencode "limit=2" \
  | jq '{data: [.data[] | {queueId, projectName, capacityMw, status}], pagination}'
{
  "data": [
    {
      "queueId": "LG-24-3-8",
      "projectName": "Ne100S",
      "capacityMw": 100,
      "status": "Active"
    },
    {
      "queueId": "Q26-LG-3-26-2",
      "projectName": "NN150S",
      "capacityMw": 150,
      "status": "Active"
    }
  ],
  "pagination": {
    "limit": 2,
    "offset": 0,
    "totalResultCount": 572,
    "nextOffset": 2
  }
}

Hybrid projects are their own generation type. Solar plus storage:

curl -s -G "https://api.gridtracker.io/v2/transmission-grid-interconnection-queue-projects" \
  -H "X-API-Key: gtapi_<your key>" \
  --data-urlencode "generationTypes=Battery + Solar" \
  --data-urlencode "limit=2" \
  | jq '{data: [.data[] | {queueId, capacityMw, canonicalGenerationTypes}], pagination}'

Repeat a parameter for several values. Projects in either of two power markets:

curl -s -G "https://api.gridtracker.io/v2/transmission-grid-interconnection-queue-projects" \
  -H "X-API-Key: gtapi_<your key>" \
  --data-urlencode "powerMarkets=CAISO" \
  --data-urlencode "powerMarkets=ERCOT" \
  --data-urlencode "limit=2" \
  | jq '{data: [.data[] | {queueId, powerMarket}], pagination}'

A sorted page

The five largest projects in ERCOT:

curl -s -G "https://api.gridtracker.io/v2/transmission-grid-interconnection-queue-projects" \
  -H "X-API-Key: gtapi_<your key>" \
  --data-urlencode "powerMarkets=ERCOT" \
  --data-urlencode "sortBy=capacityMw" \
  --data-urlencode "sortOrder=desc" \
  --data-urlencode "limit=5" \
  | jq '{data: [.data[] | {queueId, capacityMw, status}], pagination}'
{
  "data": [
    {
      "queueId": "28INR0456",
      "capacityMw": 3000,
      "status": "Active"
    },
    {
      "queueId": "30INR0091",
      "capacityMw": 1981.2,
      "status": "Active"
    },
    {
      "queueId": "30INR0071",
      "capacityMw": 1660.4,
      "status": "Active"
    },
    {
      "queueId": "27INR0061",
      "capacityMw": 1542,
      "status": "Active"
    },
    {
      "queueId": "27INR0084",
      "capacityMw": 1536.4,
      "status": "Active"
    }
  ],
  "pagination": {
    "limit": 5,
    "offset": 0,
    "totalResultCount": 3424,
    "nextOffset": 5
  }
}

Pagination

Follow nextOffset until it is null. This loop walks every active ERCOT project, 100 at a time, and prints one line per page.

offset=0
while [ "$offset" != "null" ]; do
  page=$(curl -s -G "https://api.gridtracker.io/v2/transmission-grid-interconnection-queue-projects" \
    -H "X-API-Key: gtapi_<your key>" \
    --data-urlencode "powerMarkets=ERCOT" \
    --data-urlencode "statuses=Active" \
    --data-urlencode "limit=100" \
    --data-urlencode "offset=$offset")
  echo "offset $offset: $(printf '%s' "$page" | jq '.data | length') projects, $(printf '%s' "$page" | jq '.pagination.totalResultCount') in all"
  offset=$(printf '%s' "$page" | jq -r '.pagination.nextOffset')
done
offset 0: 100 projects, 1791 in all
offset 100: 100 projects, 1791 in all
offset 200: 100 projects, 1791 in all
offset 300: 100 projects, 1791 in all
offset 400: 100 projects, 1791 in all
offset 500: 100 projects, 1791 in all
offset 600: 100 projects, 1791 in all
offset 700: 100 projects, 1791 in all
offset 800: 100 projects, 1791 in all
offset 900: 100 projects, 1791 in all
offset 1000: 100 projects, 1791 in all
offset 1100: 100 projects, 1791 in all
offset 1200: 100 projects, 1791 in all
offset 1300: 100 projects, 1791 in all
offset 1400: 100 projects, 1791 in all
offset 1500: 100 projects, 1791 in all
offset 1600: 100 projects, 1791 in all
offset 1700: 91 projects, 1791 in all

One project

Fetch a list page to get a uniqueId, then ask for that project:

unique_id=$(curl -s "https://api.gridtracker.io/v2/transmission-grid-interconnection-queue-projects?limit=1" \
  -H "X-API-Key: gtapi_<your key>" | jq -r '.data[0].uniqueId')

curl -s "https://api.gridtracker.io/v2/transmission-grid-interconnection-queue-projects/$unique_id" \
  -H "X-API-Key: gtapi_<your key>" \
  | jq '.data | {queueId, projectName, powerMarket, status, capacityMw, projectLinkInGridtracker}'
{
  "queueId": "P0693",
  "projectName": "P0693 Wild Rose 2 Wind Farm",
  "powerMarket": "AESO",
  "status": "Operational",
  "capacityMw": 193,
  "projectLinkInGridtracker": "https://www.gridtracker.io/project/aeso-p0693"
}

A filtered facility list

Data center facilities under construction in Virginia with at least 100 MW, largest first, two to a page:

curl -s -G "https://api.gridtracker.io/v2/data-center-facilities" \
  -H "X-API-Key: gtapi_<your key>" \
  --data-urlencode "states=VA" \
  --data-urlencode "facilityStatuses=Construction" \
  --data-urlencode "minPowerCapacityMw=100" \
  --data-urlencode "sortBy=powerCapacityMw" \
  --data-urlencode "sortOrder=desc" \
  --data-urlencode "limit=2" \
  | jq '{data: [.data[] | {facilityId, facilityName, city, facilityStatus, powerCapacityMw, officialRecordCount}], pagination}'
{
  "data": [
    {
      "facilityId": "99324bfe04df",
      "facilityName": "Data Center 10",
      "city": "Sterling",
      "facilityStatus": "Construction",
      "powerCapacityMw": 1000,
      "officialRecordCount": 0
    },
    {
      "facilityId": "2a1f4b4f96be",
      "facilityName": "VA1 Data Center Building 2",
      "city": "Ruther Glen",
      "facilityStatus": "Construction",
      "powerCapacityMw": 900,
      "officialRecordCount": 1
    }
  ],
  "pagination": {
    "limit": 2,
    "offset": 0,
    "totalResultCount": 21,
    "nextOffset": 2
  }
}

The first facility has no official record behind it: it is known from directory listings only, and so is its capacity. The second has one record.

Facilities near a point

Facilities within five miles of a point in Hillsboro, Oregon, nearest first:

curl -s -G "https://api.gridtracker.io/v2/data-center-facilities" \
  -H "X-API-Key: gtapi_<your key>" \
  --data-urlencode "latitude=45.54" \
  --data-urlencode "longitude=-122.95" \
  --data-urlencode "radiusMiles=5" \
  --data-urlencode "sortBy=distanceMiles" \
  --data-urlencode "limit=3" \
  | jq '{data: [.data[] | {facilityId, facilityName, city, distanceMiles, latLongPrecision}], pagination}'
{
  "data": [
    {
      "facilityId": "6fbc407437a8",
      "facilityName": "OVHcloud HIL1",
      "city": "Hillsboro",
      "distanceMiles": 0.47,
      "latLongPrecision": "direct"
    },
    {
      "facilityId": "747dc99066dc",
      "facilityName": "HI3",
      "city": "Hillsboro",
      "distanceMiles": 0.77,
      "latLongPrecision": "direct"
    },
    {
      "facilityId": "bbf6490a6db3",
      "facilityName": "Aligned PDX-01",
      "city": "Hillsboro",
      "distanceMiles": 0.8,
      "latLongPrecision": "direct"
    }
  ],
  "pagination": {
    "limit": 3,
    "offset": 0,
    "totalResultCount": 88,
    "nextOffset": 3
  }
}

distanceMiles is on every facility in the response because the filter is present. Without the filter it is absent, and sortBy=distanceMiles is refused (see Each error).

One facility with its official records

Gainesville Crossing Building 1 has ten official records, and a facility fetch inlines all of them:

curl -s "https://api.gridtracker.io/v2/data-center-facilities/10cd3fc5fc50" \
  -H "X-API-Key: gtapi_<your key>" \
  | jq '.data | {facilityId, facilityName, campusName, officialRecordCount, officialRecordsTruncated, inlined: (.officialRecords | length), firstInlined: (.officialRecords[0] | {recordId, permitType, applicationDate})}'
{
  "facilityId": "10cd3fc5fc50",
  "facilityName": "Gainesville Crossing Building 1",
  "campusName": "Corscale Gainesville Crossing Data Campus (Gainesville, VA)",
  "officialRecordCount": 10,
  "officialRecordsTruncated": false,
  "inlined": 10,
  "firstInlined": {
    "recordId": "7b0e348aca18",
    "permitType": "Code Mod Commercial New Structure",
    "applicationDate": "2021-05-21"
  }
}

The fetch cost one request and 11 rows: the facility and its ten records. To list a facility's records on their own, or to sort and page through them, ask the official records endpoint with facilityIds. The three most recently filed, newest first:

curl -s -G "https://api.gridtracker.io/v2/data-center-official-records" \
  -H "X-API-Key: gtapi_<your key>" \
  --data-urlencode "facilityIds=10cd3fc5fc50" \
  --data-urlencode "sortBy=applicationDate" \
  --data-urlencode "sortOrder=desc" \
  --data-urlencode "limit=3" \
  | jq '{data: [.data[] | {recordId, permitType, applicationDate}], pagination}'
{
  "data": [
    {
      "recordId": "e9c01f50fa7e",
      "permitType": "Zoning Approval - Occupancy Permit Approval",
      "applicationDate": "2024-08-22"
    },
    {
      "recordId": "69d1a0003883",
      "permitType": "Zoning Approval - Occupancy Permit Approval",
      "applicationDate": "2024-08-07"
    },
    {
      "recordId": "170b3b72a1bd",
      "permitType": "Fire Protection - Sprinkler Plan",
      "applicationDate": "2022-05-24"
    }
  ],
  "pagination": {
    "limit": 3,
    "offset": 0,
    "totalResultCount": 10,
    "nextOffset": 3
  }
}

That request cost three rows. One of the facility's ten records has no application date, and records without one come last in either direction, so the other nine fill the first nine places. Leaving limit out asks for 100 records, the default page, and limit=1000 asks for the most a page can hold.

Official records filed in a date range

Official records whose application date falls in the third quarter of 2025, newest first:

curl -s -G "https://api.gridtracker.io/v2/data-center-official-records" \
  -H "X-API-Key: gtapi_<your key>" \
  --data-urlencode "applicationDateFrom=2025-07-01" \
  --data-urlencode "applicationDateTo=2025-09-30" \
  --data-urlencode "sortBy=applicationDate" \
  --data-urlencode "sortOrder=desc" \
  --data-urlencode "limit=3" \
  | jq '{data: [.data[] | {recordId, state, permitType, applicationDate, facilityId}], pagination}'
{
  "data": [
    {
      "recordId": "7dd83b9ab0ef",
      "state": "VA",
      "permitType": "Cultural Resources Assessment",
      "applicationDate": "2025-09-30",
      "facilityId": "a4e20fcba999"
    },
    {
      "recordId": "f1a1a2e9cde3",
      "state": "AZ",
      "permitType": "Civil Construction Permit",
      "applicationDate": "2025-09-29",
      "facilityId": "96b4fcf2a214"
    },
    {
      "recordId": "783f6292b8d6",
      "state": "TX",
      "permitType": "Air Quality Permit",
      "applicationDate": "2025-09-29",
      "facilityId": "b73412332942"
    }
  ],
  "pagination": {
    "limit": 3,
    "offset": 0,
    "totalResultCount": 242,
    "nextOffset": 3
  }
}

Facilities aggregated by state

Facility count and total capacity per state, the five states with the most facilities first:

curl -s -G "https://api.gridtracker.io/v2/data-center-facilities/aggregate" \
  -H "X-API-Key: gtapi_<your key>" \
  --data-urlencode "groupBy=state" \
  --data-urlencode "metrics=facilityCount" \
  --data-urlencode "metrics=sumPowerCapacityMw" \
  --data-urlencode "limit=5"
{
  "data": [
    {
      "groupingValues": {
        "state": "VA"
      },
      "facilityCount": 2329,
      "sumPowerCapacityMw": 102496.449
    },
    {
      "groupingValues": {
        "state": "TX"
      },
      "facilityCount": 2092,
      "sumPowerCapacityMw": 125840.114
    },
    {
      "groupingValues": {
        "state": "CA"
      },
      "facilityCount": 710,
      "sumPowerCapacityMw": 4737.723
    },
    {
      "groupingValues": {
        "state": "OH"
      },
      "facilityCount": 703,
      "sumPowerCapacityMw": 28970.108
    },
    {
      "groupingValues": {
        "state": "GA"
      },
      "facilityCount": 666,
      "sumPowerCapacityMw": 22400.642
    }
  ],
  "aggregation": {
    "groupBy": "state",
    "metrics": ["facilityCount", "sumPowerCapacityMw"],
    "totalFacilitiesConsidered": 12999,
    "totalGroupCount": 54
  },
  "pagination": {
    "limit": 5,
    "offset": 0,
    "totalResultCount": 54,
    "nextOffset": 5
  }
}

Virginia has the most facilities and Texas the most recorded capacity. There are 54 groups: every state and province with a facility, plus the 32 facilities with no state under null. Group by the year of the first record instead, and the biggest group is the one with no year at all:

curl -s -G "https://api.gridtracker.io/v2/data-center-facilities/aggregate" \
  -H "X-API-Key: gtapi_<your key>" \
  --data-urlencode "groupBy=firstRecordYear" \
  --data-urlencode "metrics=facilityCount" \
  --data-urlencode "limit=3"
{
  "data": [
    {
      "groupingValues": {
        "firstRecordYear": null
      },
      "facilityCount": 11408
    },
    {
      "groupingValues": {
        "firstRecordYear": "2025"
      },
      "facilityCount": 217
    },
    {
      "groupingValues": {
        "firstRecordYear": "2024"
      },
      "facilityCount": 195
    }
  ],
  "aggregation": {
    "groupBy": "firstRecordYear",
    "metrics": ["facilityCount"],
    "totalFacilitiesConsidered": 12999,
    "totalGroupCount": 53
  },
  "pagination": {
    "limit": 3,
    "offset": 0,
    "totalResultCount": 53,
    "nextOffset": 3
  }
}

One official record

A record attached to a campus-level entry:

curl -s "https://api.gridtracker.io/v2/data-center-official-records/008a50835c3c" \
  -H "X-API-Key: gtapi_<your key>" \
  | jq '.data | {recordId, facilityId, campusId, developmentName, permitType, permitStatus, applicationDate, recordLinkInGridtracker}'
{
  "recordId": "008a50835c3c",
  "facilityId": "b5af76544504",
  "campusId": "qts-atlanta-1-ga",
  "developmentName": "QTS METRO- NORTH ELEVATOR UPGRADE",
  "permitType": "Commercial QCR",
  "permitStatus": "Create Application",
  "applicationDate": "2019-10-21",
  "recordLinkInGridtracker": "https://www.gridtracker.io/datacenter/permit/008a50835c3c"
}

Its facilityId is the campus-level entry for QTS Atlanta-Metro, which the facilities endpoint answers with 404 (see Each error). campusIds=qts-atlanta-1-ga on the facilities endpoint lists the campus's buildings, and facilityIds=b5af76544504 on this endpoint lists the entry's 119 records.

The snapshot version

Read the header from any data center response:

curl -s -D - -o /dev/null \
  "https://api.gridtracker.io/v2/data-center-facilities?limit=1" \
  -H "X-API-Key: gtapi_<your key>" \
  | grep -i data-center-data-version
x-gridtracker-data-center-data-version: 2026-10-01-ac2cac2f6836

Each error

Most of these print the response body and then the HTTP status.

A request with no key (MISSING_API_KEY, 401):

curl -s -w "\nHTTP %{http_code}\n" \
  "https://api.gridtracker.io/v2/transmission-grid-interconnection-queue-projects?limit=1"
{
  "error": {
    "code": "MISSING_API_KEY",
    "message": "No API key was sent. Send your key in the X-API-Key header."
  }
}

A key that is not valid (INVALID_API_KEY, 401):

curl -s -w "\nHTTP %{http_code}\n" \
  "https://api.gridtracker.io/v2/transmission-grid-interconnection-queue-projects?limit=1" \
  -H "X-API-Key: gtapi_aaaaaaaaaaaa_not-a-real-key"
{
  "error": {
    "code": "INVALID_API_KEY",
    "message": "The API key in the X-API-Key header is not valid or is no longer active."
  }
}

A parameter that is not valid (INVALID_PARAMETER, 400). There is no developer sort:

curl -s -w "\nHTTP %{http_code}\n" -G \
  "https://api.gridtracker.io/v2/transmission-grid-interconnection-queue-projects" \
  -H "X-API-Key: gtapi_<your key>" \
  --data-urlencode "sortBy=developers"
{
  "error": {
    "code": "INVALID_PARAMETER",
    "message": "Invalid value for parameter \"sortBy\": must be one of queueId, powerMarket, capacityMw, canonicalGenerationTypes, status, projectName, proposedCompletionDate, actualCompletionDate",
    "parameter": "sortBy"
  }
}

A project that does not exist (NOT_FOUND, 404):

curl -s -w "\nHTTP %{http_code}\n" \
  "https://api.gridtracker.io/v2/transmission-grid-interconnection-queue-projects/does-not-exist" \
  -H "X-API-Key: gtapi_<your key>"
{
  "error": {
    "code": "NOT_FOUND",
    "message": "No item in transmission-grid-interconnection-queue-projects has the id \"does-not-exist\"."
  }
}

A path the API does not serve (NOT_FOUND, 404):

curl -s -w "\nHTTP %{http_code}\n" "https://api.gridtracker.io/dashboards"
{
  "error": {
    "code": "NOT_FOUND",
    "message": "No endpoint exists at this path."
  }
}

A retired version (API_VERSION_RETIRED, 410). The Sunset header is in the response headers:

curl -s -i "https://api.gridtracker.io/v1/projects"
{
  "error": {
    "code": "API_VERSION_RETIRED",
    "message": "API version v1 has been retired. The current version is v2. Key holders received the v2 user guide with their key; ask [email protected] for a copy."
  }
}

A request that could return more rows than the key has left in the minute (ROW_LIMIT_EXCEEDED, 429) is refused, even if fewer rows would have matched. The response carries Retry-After, the seconds to wait, and X-RateLimit-Rows-Remaining, the largest limit the key can ask for right now. Below is the shape of the response. It has no command because the check happens before any row is fetched: a page is refused when its limit is larger than the rows the key has left in the minute, so producing the refusal on purpose means spending most of a minute's allowance first:

{
  "error": {
    "code": "ROW_LIMIT_EXCEEDED",
    "message": "This key does not have enough rows left this minute for the requested page size. Ask for a smaller page, or retry after the number of seconds in the Retry-After header."
  }
}

Once a key has made all of its requests for the minute (REQUEST_LIMIT_EXCEEDED, 429), 300 at the limits above, the next one is refused. The response carries Retry-After, and X-RateLimit-Requests-Remaining is 0. Wait out Retry-After, or spread your requests over more of the minute. This is the shape of that response, and it has no command either:

{
  "error": {
    "code": "REQUEST_LIMIT_EXCEEDED",
    "message": "This key has used all of its requests for the current minute. Retry after the number of seconds in the Retry-After header."
  }
}

A campus-level entry's id on the facilities endpoint (NOT_FOUND, 404). The id comes from an official record's facilityId:

curl -s -w "\nHTTP %{http_code}\n" \
  "https://api.gridtracker.io/v2/data-center-facilities/b5af76544504" \
  -H "X-API-Key: gtapi_<your key>"
{
  "error": {
    "code": "NOT_FOUND",
    "message": "No item in data-center-facilities has the id \"b5af76544504\"."
  }
}

A proximity filter with a parameter missing (INVALID_PARAMETER, 400):

curl -s -w "\nHTTP %{http_code}\n" -G \
  "https://api.gridtracker.io/v2/data-center-facilities" \
  -H "X-API-Key: gtapi_<your key>" \
  --data-urlencode "latitude=45.54" \
  --data-urlencode "longitude=-122.95"
{
  "error": {
    "code": "INVALID_PARAMETER",
    "message": "Invalid value for parameter \"radiusMiles\": is required when latitude and longitude are given: the proximity filter needs latitude, longitude and radiusMiles together",
    "parameter": "radiusMiles"
  }
}

A sort on distance without the proximity filter (INVALID_PARAMETER, 400):

curl -s -w "\nHTTP %{http_code}\n" -G \
  "https://api.gridtracker.io/v2/data-center-facilities" \
  -H "X-API-Key: gtapi_<your key>" \
  --data-urlencode "sortBy=distanceMiles"
{
  "error": {
    "code": "INVALID_PARAMETER",
    "message": "Invalid value for parameter \"sortBy\": distanceMiles can only be sorted on when the proximity filter (latitude, longitude and radiusMiles) is present",
    "parameter": "sortBy"
  }
}

The last three codes are not ones you can ask for. They look like this:

{
  "error": {
    "code": "TOO_MANY_FAILED_AUTHENTICATIONS",
    "message": "Too many requests with a missing or invalid API key came from this address. Retry after the number of seconds in the Retry-After header."
  }
}
{
  "error": {
    "code": "SERVICE_UNAVAILABLE",
    "message": "The data store is temporarily unavailable. Retry shortly."
  }
}
{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "The server could not build a valid response for this request. The failure has been logged."
  }
}

Support

For a lost key, a limit that is too low for your use, or a response that looks wrong, email [email protected]. Include the key's lookup prefix (the 12 characters after gtapi_), the time of the request and the URL. Never send the full key.