If your support team works from a shared Gmail inbox, Hiver may already be managing it. What fewer teams know is that Hiver has a REST API, so other tools can read and update what is in your inboxes.
This guide sticks to what is in Hiver's official developer documentation (checked on 15 October 2026), and it tells you where to look for the details the docs leave out. Unlike many tutorials, it does not guess at endpoints.
What is Hiver?#
Hiver is a shared inbox and helpdesk tool that works inside Gmail. Instead of a separate support platform, your team handles customer email in the Gmail interface they already use, and Hiver adds assignment, tags, notes and reporting on top.
For developers, the main objects are:
- Inbox: a shared mailbox, such as
[email protected] - Conversation: an email thread inside an inbox
- User: a teammate with access to the inbox
- Tag: a label you can put on conversations
- Note: an internal comment on a conversation (customers never see it)
Getting your API key#
Hiver's documentation says an admin creates API keys:
- Open your Hiver admin panel.
- Go to Developer settings.
- Click Create app and give it a name.
- Copy the generated key.
export HIVER_API_KEY="your_api_key_here"Your first request#
Hiver's docs show the inboxes endpoint under the api2.hiverhq.com host. Authentication is a Bearer token:
curl https://api2.hiverhq.com/v1/inboxes \
-H "Authorization: Bearer $HIVER_API_KEY"A successful call returns HTTP 200 with your data wrapped in a data object. If you get 401 or 403, the key is wrong or not allowed. Hiver also provides a sandbox environment, so test there before touching real inboxes. Confirm the exact production and sandbox addresses in Hiver's API Explorer.
What the API can do#
Hiver's reference lists these endpoints:
Inbox
| Method | What it does |
|---|---|
GET | List all the inboxes |
GET | Get one inbox by ID |
GET | Get all users in an inbox |
GET | Search users in an inbox |
GET | Get tags in an inbox |
GET | Search tags in an inbox |
POST | Create a tag in an inbox |
Conversations
| Method | What it does |
|---|---|
GET | Get conversations in an inbox |
GET | Get one conversation |
PATCH | Update a conversation (status, assignee and tags fields) |
POST | Create a shared draft for a conversation |
POST | Create a note on a conversation |
Hiver's reference pages show the endpoint names and parameters but not every full URL path on the page itself. For exact paths and field values, use the API Explorer or the Download API Spec option in Hiver's developer docs, and build your client from that. That is more reliable than copying paths from a blog post, including this one.
Things the docs tell you#
- Conversation IDs: a conversation can be identified by a Hiver conversation ID or a Gmail thread ID.
user_email(optional query parameter): needed only when you use a Gmail thread ID from that user's own mailbox. If you leave it out, the thread ID is treated as the shared mailbox's thread ID.- Updating a conversation: the request body has
status,assigneeandtagsobjects. A successful update returns204with no body. - Adding a note: the body takes
content(plain text, emoji and a safe subset of HTML), optionalmentions(a list of teammate emails),notify_all,parent_note_idfor replies, and an optionalattachmentsfile. A successful call returns201.
Rate limits: the most important rule#
Hiver's documented limit is 1 request per second per account, plus 5,000 requests per day. Going over returns 429, and Hiver warns that continuous retries can get your IP address or API key blocked.
So build the limit into your code from the start. Here is a small helper that waits between calls and backs off on 429:
// Node.js 18+ (uses the built-in fetch)
const BASE = "https://api2.hiverhq.com/v1";
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
let lastCall = 0;
async function hiver(path, options = {}, attempt = 0) {
// Stay under 1 request per second.
const wait = Math.max(0, lastCall + 1100 - Date.now());
if (wait) await sleep(wait);
lastCall = Date.now();
const res = await fetch(`${BASE}${path}`, {
...options,
headers: {
Authorization: `Bearer ${process.env.HIVER_API_KEY}`,
"Content-Type": "application/json",
...(options.headers ?? {}),
},
});
if (res.status === 429 && attempt < 5) {
await sleep(2 ** attempt * 1000); // exponential backoff: 1s, 2s, 4s...
return hiver(path, options, attempt + 1);
}
if (!res.ok) throw new Error(`Hiver API error ${res.status}`);
return res.status === 204 ? null : res.json();
}
const inboxes = await hiver("/inboxes");
console.log(inboxes.data);import os
import time
import requests
BASE = "https://api2.hiverhq.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['HIVER_API_KEY']}"}
_last_call = 0.0
def hiver(method: str, path: str, **kwargs):
global _last_call
for attempt in range(5):
# Stay under 1 request per second.
wait = _last_call + 1.1 - time.time()
if wait > 0:
time.sleep(wait)
_last_call = time.time()
res = requests.request(method, f"{BASE}{path}", headers=HEADERS, timeout=15, **kwargs)
if res.status_code == 429:
time.sleep(2 ** attempt) # exponential backoff
continue
res.raise_for_status()
return None if res.status_code == 204 else res.json()
raise RuntimeError("Hiver API kept returning 429")
print(hiver("GET", "/inboxes")["data"])Pagination#
List endpoints are paginated. According to Hiver's docs:
limitsets the page size, from 10 to 100 (default 10).sort_byandsort_order(ascordesc, defaultasc) control ordering. Which fields you can sort by depends on the endpoint.- Lists come back as
data.results, with the next page token indata.pagination.next_page. - Request the next page with
next_pageuntil it isnull.
async function listAll(path) {
const items = [];
let next = null;
do {
const query = new URLSearchParams({ limit: "100" });
if (next) query.set("next_page", next);
const page = await hiver(`${path}?${query}`);
items.push(...page.data.results);
next = page.data.pagination.next_page;
} while (next);
return items;
}Remember the daily cap: with 100 items per page, fetching a very large inbox can use up a lot of your 5,000 daily requests, so cache what you read.
Errors#
Hiver documents these status codes:
| Code | Meaning |
|---|---|
200 | Success |
204 | Success, no body |
400 | Missing required parameters |
401 / 403 | Not authorised |
404 | Not found |
429 | Rate limit hit: slow down and retry with backoff |
500 | Hiver-side error: check status.hiverhq.com or contact Hiver |
Error bodies come in two shapes: an errors array of objects with a message field, or a single Message field. Write your error handler to accept both.
What you can build#
- Internal dashboards: pull inboxes, tags and conversations into your own reports.
- Triage helpers: read new conversations and add an internal note or update the assignee, status or tags with
PATCH. - Shared drafts: prepare a draft reply for a teammate to review.
- Exports and backups: copy conversation data into your own database on a schedule.
Because the API is limited to 1 request per second and 5,000 a day, it suits scheduled jobs and light automation. It is not a good fit for anything that needs to react in real time, and the docs do not mention webhooks.
Before you go live#
- You built your client from Hiver's API Explorer or API spec, not from guessed paths
- The key is stored as a secret and was created for this one app
- You tested in the sandbox first
- Your code waits at least 1 second between calls
- 429 responses back off instead of retrying instantly
- You cache reads so you stay under 5,000 requests a day
- Your error handler accepts both error body shapes
Related reading#
- Best Email APIs for Developers: if you need transactional email alongside your helpdesk integration.
- Twilio SMS API Tutorial: adding SMS notifications to your support workflow.
- API Design Principles: why clear limits, good errors and pagination matter in any API.
- Browse all Communication APIs: email, SMS, voice and helpdesk APIs.