API - Errors and Rate Limits

This article explains how the API tells you that something went wrong, what each status code and message means, and how the rate limits work. It applies to every API6 endpoint.


How to Tell Whether a Call Worked

Every answer from the API has an HTTP status code. Check the status code first, before you read the body.

  • A code in the 200s means the call worked.
  • A code in the 400s means something was wrong with the request. The body tells you what.
  • A code in the 500s means something went wrong on our side.

An error body is JSON with the status code and a message:

{
  "status": 403,
  "message": "Permission access restricted"
}

A successful create or update that has no other data to return looks like this. recordid is the ID of the record that was created or updated:

{
  "status": 201,
  "message": "Created",
  "recordid": 533297,
  "request_time": 1760000000
}

request_time is added to successful answers. It is the time the API answered, as a Unix timestamp in seconds.


Status Codes

CodeNameWhat it means
200OKThe call worked
201CreatedA new record was created. Its ID is in recordid
400Bad RequestA required value is missing or a value is not valid. The message names the problem
401UnauthorizedThe API key is missing, wrong, or not allowed to be used from your address
403ForbiddenThe API key is valid but is not allowed to do this
404Not FoundThe record or the endpoint does not exist, or a list has no results
405Method Not AllowedThe endpoint does not accept that HTTP method, for example a GET where a POST is needed
409ConflictA duplicate was detected. recordid holds the ID of the record that already exists
410GoneThe call went to API version 1 through 5, which have been shut down. See API - Migration (API1-5 to API6)
422Unprocessable ContentUsed by a few endpoints when required values are missing
429Too Many RequestsA rate limit was reached. See Rate Limits below
500Internal Server ErrorSomething went wrong on our side. If it keeps happening, contact support with the URL you called and the time

Common Messages and How to Fix Them

StatusMessageHow to fix it
401No API Key providedAdd your key as the apiKey parameter. The name is case sensitive: a capital K
401API key not foundThe key is mistyped or was removed. Copy it again from Settings > API Keys
401IP restrictionThe key has IP Restrictions and the call came from an address that is not on the list. Add the address of your server to the key
401Subscription doesn't include API accessActivate the API & WordPress module under Settings > Subscription
403Permission access restrictedOpen the key under Settings > API Keys and grant the permission the endpoint needs. Each endpoint article lists it
403Location access restrictedThe record belongs to a location the key cannot access. Give the key access to that location
404Customer not found, Lead not found, Item not foundCheck the ID in the URL. The record must belong to your account
404No customers found, No rentals found, No leads found - Lead ListA list call matched nothing, or you asked for a page past the end of the list
404(no message, only request_time)The endpoint name in the URL is wrong. Check the spelling after /api6/
405Only accepts GET requests, Only accepts POST requests, Invalid MethodUse the HTTP method the endpoint article shows
409Conflict: Duplicate detectedThe record already exists. Use the ID in recordid instead of creating it again
429Too Many RequestsWait for the number of seconds in the Retry-After header, then slow your integration down

Starting with the October 2026 release you may also see these:

403 "Your subscription does not include the ... module required for the /... endpoint." Your subscription is missing the package the endpoint belongs to. See API - Packages and Endpoint Access.

429 "Daily API call limit reached" or "Monthly API cost limit reached. Raise the limit from Settings > API Preferences." The account reached a usage limit set under Settings > API Preferences.


Rate Limits

Rate limits protect your account and our servers from an integration that calls too fast. They are counted per API key.

LimitWhat happens when you reach it
15 requests in 10 secondsRequests are refused with 429 for a 20 second cooldown
300 requests in 300 seconds (5 minutes)Requests are refused with 429 until enough time passes that you are under the limit again
  • A refused request includes a Retry-After header with the number of seconds to wait.
  • Refused requests are not counted as API transactions.
  • The account owner is emailed when a limit is reached, at most once per hour.

Starting with the October 2026 release: the cooldown becomes 60 seconds, and a third limit of 2 requests per second is added. If your code waits for the time in the Retry-After header and spaces its calls at least half a second apart, it will keep working without changes.


Handling a 429 in Code

The safest pattern is: when you get a 429, read Retry-After, wait that long, and try the same call again. Give up after a few tries so a problem cannot turn into an endless loop.

<?php
function io_api_get($url) {
    for ($attempt = 1; $attempt <= 3; $attempt++) {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HEADER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        $raw = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        curl_close($ch);

        $headers = substr($raw, 0, $header_size);
        $body = substr($raw, $header_size);

        if ($status != 429) {
            return array($status, json_decode($body, true));
        }

        // Rate limited: wait as long as the API asks, then try again.
        $wait = 20;
        if (preg_match('/^Retry-After:\s*(\d+)/mi', $headers, $match)) {
            $wait = (int)$match[1];
        }
        sleep($wait);
    }
    return array(429, null);
}

list($status, $data) = io_api_get('https://rental.software/api6/rentals?apiKey=YOUR_API_KEY');

Tips to Stay Under the Limits

  • Space out your calls. When looping over many records, pause briefly between calls instead of sending them all at once.
  • Ask only for what changed. Use changes_since on the rentals list and modified_date on the leads list instead of downloading everything each time.
  • Save what does not change often. Categories, statuses and inventory details rarely change. Store them on your side and refresh them once a day, not on every page view.
  • Use bigger pages. One call with limit=100 is one request. Four calls with limit=25 are four.
  • Stop at the last page. When a page comes back with fewer items than your limit, you are done. Asking for the next page returns a 404.
  • Tell your AI tools. If you connect an AI assistant or agent to the API, give it these limits so it does not flood your account with calls.

Related Articles

Is this article helpful?
0 0 0