Request logs
Read the metadata for every API call your organization made: method, path, status code, and duration.
Every API call leaves a record of its method, its path, its status code, and how long it took. Use it to debug an integration, trace which key did what, or find the pattern behind a run of errors.
The log stores metadata, not content. It never returns the IP address, the user agent, or your query-string values.
Read your recent calls
| Method | Path | Access |
|---|---|---|
GET |
/v1/api-requests |
Any authenticated member, or a key with api_keys:read |
A dashboard session gets in regardless of role: VIEWER can read it too. With an API key you need the api_keys:read scope (or *).
curl "https://api.sendit.mx/v1/api-requests?method=POST&statusCode=402&limit=20" \
-H "X-API-Key: sk_test_..."
Query parameters
method?string
Filter by HTTP method, for example POST.
stringstatusCode?integer
Filter by exact status code, for example 402.
integerapiKeyId?string
Filter by the key that made the call.
stringafter?string
ISO-8601 date. Only requests created after that moment.
stringbefore?string
ISO-8601 date. Only requests created before that moment.
stringcursor?string
Opaque cursor from the previous page. Pass it back unchanged.
stringlimit?integer
How many records to return per page.
integer{
"success": true,
"data": {
"data": [
{
"id": "req_01J8Z9K2M4N6P8Q0R2S4T6",
"apiKeyId": "key_a1b2c3d4e5f6",
"method": "POST",
"path": "/v1/shipments",
"statusCode": 201,
"durationMs": 142,
"requestId": "01J8Z9K2M4N6P8Q0R2S4T6",
"livemode": true,
"createdAt": "2026-08-03T18:00:00.000Z"
}
],
"total": 1,
"nextCursor": null,
"hasMore": false
}
}
The apiKeyId, durationMs, and requestId fields can arrive as null.
| Field | Description |
|---|---|
id |
Identifier of the log row |
apiKeyId |
The key that made the call; null when it came from a dashboard session |
method |
HTTP method |
path |
Path that was called |
statusCode |
Status code we returned |
durationMs |
How long the request took |
requestId |
The same identifier we return on errors |
livemode |
false when the call happened in test mode |
createdAt |
When the request arrived |
Page through the results
Results come back newest first. Pass nextCursor back unchanged and stop when hasMore is false:
let cursor = null;
do {
const url = new URL("https://api.sendit.mx/v1/api-requests");
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, { headers: { "X-API-Key": process.env.SENDIT_KEY } });
const { data } = await res.json();
for (const row of data.data) process(row);
cursor = data.hasMore ? data.nextCursor : null;
} while (cursor);
Separate test from live
Results are always scoped to the active mode. With an API key, the key’s own environment fixes the mode. With a dashboard session, add ?livemode=false to read test-mode requests.
The endpoint excludes itself, so reading the log never creates new rows. You can poll it without polluting your own data.
Handle errors
| Code | When it happens | How to resolve it |
|---|---|---|
400 VALIDATION_ERROR |
A filter carries an invalid value, for example a date that is not ISO-8601 | Correct the parameter and retry |
401 UNAUTHORIZED |
The credential is missing or invalid | Send a valid key, or sign in |
403 INSUFFICIENT_SCOPE |
The key lacks api_keys:read |
Issue a key with that scope |
429 RATE_LIMIT_EXCEEDED |
You exceeded the read limit | Respect the rate-limit headers before retrying |