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
| Code | Name | What it means |
|---|---|---|
| 200 | OK | The call worked |
| 201 | Created | A new record was created. Its ID is in recordid |
| 400 | Bad Request | A required value is missing or a value is not valid. The message names the problem |
| 401 | Unauthorized | The API key is missing, wrong, or not allowed to be used from your address |
| 403 | Forbidden | The API key is valid but is not allowed to do this |
| 404 | Not Found | The record or the endpoint does not exist, or a list has no results |
| 405 | Method Not Allowed | The endpoint does not accept that HTTP method, for example a GET where a POST is needed |
| 409 | Conflict | A duplicate was detected. recordid holds the ID of the record that already exists |
| 410 | Gone | The call went to API version 1 through 5, which have been shut down. See API - Migration (API1-5 to API6) |
| 422 | Unprocessable Content | Used by a few endpoints when required values are missing |
| 429 | Too Many Requests | A rate limit was reached. See Rate Limits below |
| 500 | Internal Server Error | Something 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
| Status | Message | How to fix it |
|---|---|---|
| 401 | No API Key provided | Add your key as the apiKey parameter. The name is case sensitive: a capital K |
| 401 | API key not found | The key is mistyped or was removed. Copy it again from Settings > API Keys |
| 401 | IP restriction | The 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 |
| 401 | Subscription doesn't include API access | Activate the API & WordPress module under Settings > Subscription |
| 403 | Permission access restricted | Open the key under Settings > API Keys and grant the permission the endpoint needs. Each endpoint article lists it |
| 403 | Location access restricted | The record belongs to a location the key cannot access. Give the key access to that location |
| 404 | Customer not found, Lead not found, Item not found | Check the ID in the URL. The record must belong to your account |
| 404 | No customers found, No rentals found, No leads found - Lead List | A 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/ |
| 405 | Only accepts GET requests, Only accepts POST requests, Invalid Method | Use the HTTP method the endpoint article shows |
| 409 | Conflict: Duplicate detected | The record already exists. Use the ID in recordid instead of creating it again |
| 429 | Too Many Requests | Wait 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.
| Limit | What happens when you reach it |
|---|---|
| 15 requests in 10 seconds | Requests 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.