Skip to main content

Limits, versioning, and support

Rate limits​

The API enforces the limits below. A request that exceeds one is rejected rather than run.

LimitValueApplies to
Requests per API key300 per minuteEvery query request to the external API.
Occupancy time-series per API key45 per minuteoccupancyPerMinute, occupancyPerFiveMinutes, occupancyPerHour, occupancyPerDay and their ...ForPreviousDay variants. Each selection counts, so a request that selects three of these fields uses three.
Aliases of one occupancy time-series field5 per requestEach of the occupancy time-series fields above.
Entry crossings per API key60 per minuteentryCrossings. Each selection counts, so a request that selects it under three aliases uses three.
Query depth15 levels of nestingEvery request.
Field selections2,000 per request, after fragments are expandedEvery request.

Per-minute limits use a fixed one-minute window: the count resets at the start of each window rather than sliding.

When a limit is exceeded​

The request is rejected and the response carries an errors entry naming the limit, for example:

  • Rate limit exceeded for policy 'apiBudget'. - the per-key request limit,
  • Rate limit exceeded for policy 'occupancyPerPeriod'. - the occupancy time-series limit,
  • Rate limit exceeded for policy 'heavyQuery'. - the entry crossings limit,
  • Query exceeds maximum allowed depth of 15 or a similar message for the depth, field selection and alias limits.

A rate-limit rejection returns HTTP status 429 Too Many Requests with a Retry-After header giving the seconds to wait before retrying. A query rejected for depth, field selections or aliases returns 422 Unprocessable Content. Other errors keep their usual status codes; see Errors. When you hit a rate limit, wait for the Retry-After period before retrying, and back off exponentially rather than retrying in a tight loop. A query rejected for depth, field selections or aliases will fail the same way every time, so split it rather than retrying it.

Staying within the limits​

The patterns below keep an integration well inside the limits, and fast:

  • Match your polling rate to how often the data changes. Occupancy and sightings update at most every ~2 seconds, and polling occupancyChanges every 30 seconds with a from/nextFrom cursor is a good default. Analytics updates once a day, so a daily poll is enough.
  • Prefer webhooks for frequent changes. If you find yourself polling often to keep up, a webhook will deliver the same data with lower latency and less work on both sides.
  • Prefer fewer, larger queries. One building- or floor-scoped query is cheaper than many per-space queries, even if it fetches more than you need. Aliased queries in a single request execute sequentially, so batching by scope (floor, building) is faster and avoids timeouts.
  • Fetch occupancy time-series by floor or building, not per space, so a full refresh stays within the 45-per-minute occupancy limit.
  • Request windows and retention limits per dataset are listed in Available data.

If you have a workload that needs more than these limits allow (large portfolio backfills, or polling faster than every 30 seconds), reach out before you build and we will help you find the most efficient approach.

API versioning​

There is no version in the API URL, and no version header to send. A single current version of the GraphQL schema is served, and integrations do not pin to a version.

That works because the schema only grows: see Schema stability and breaking changes for what is additive, what counts as breaking, and how breaking changes are communicated. If we ever need to introduce versioning, it will be announced well ahead of time along with a migration path for existing integrations.

Deprecated fields​

Where a field, argument, or type is superseded, we keep the original working and mark it deprecated rather than removing it, so existing integrations keep running. This is how we avoid needing an API version.

A deprecation is marked in the schema itself and carries a reason naming what to use instead. The API currently has a few, for example:

  • location and locations are deprecated in favour of building and buildings,
  • on SpaceGroup, spaces and totalSpaces are deprecated in favour of floorSpaces and totalFloorSpaces.

If you are building a new integration, do not use deprecated fields. They are the likely candidates for removal in future, and removal is a breaking change, so it would come with advance notice and a migration window. Choosing the current field now saves you that migration. If you maintain an existing integration, there is no urgency to move, but moving off a deprecated field when you next touch the code is worthwhile.

Schema stability and breaking changes​

The external GraphQL schema evolves additively: new fields, arguments, types, and enum values are added over time, and existing ones are not removed or repurposed without warning. Extending rather than changing the API is a deliberate choice, and it is why there is no API version for you to pin.

Changes we make without notice, and that your client should tolerate:

  • new fields and types in the schema,
  • new optional arguments on existing fields,
  • new values in an existing enum,
  • new event types and new fields in webhook payloads,
  • marking an existing field as deprecated, which does not change its behaviour (see Deprecated fields).

Changes we treat as breaking, and do not make without notice:

  • removing or renaming a field, argument, type, or enum value,
  • changing the type or nullability of an existing field,
  • changing the meaning of an existing field, or the units it is expressed in,
  • making a previously optional argument required.

To stay compatible, request only the fields you use, ignore fields you do not recognise, and handle an unfamiliar enum value as a default case rather than an error.

When a breaking change is unavoidable, XY Sense notifies registered API contacts ahead of time with a migration window, so you have time to adjust before the change takes effect. Planned maintenance that affects API availability is communicated the same way, ahead of the window.

Make sure we hold the right technical contact for your integration, so these notices reach the person who maintains it rather than a commercial contact.

The generated API reference always reflects the current production schema.

Getting help​

  • Integration questions or unexpected results: contact your XY Sense representative or reach us via support@xysense.com.
  • Include your query (with variables), the full response including any errors array, and a timestamp. That is usually enough for us to reproduce.
  • Feature enablement (Occupancy API, Analytics API, Entry Crossings API, Webhooks) and dev-mode floors are provisioned by XY Sense, ask your representative or contact us via support@xysense.com.