Best practices
Recommended patterns for pagination, error handling, retries, and more
Overview
This guide collects best practices for a reliable Conta Simples API integration. It covers pagination, errors, retries, and more — without duplicating per-endpoint details, which live in the API Reference.
Pagination
The API uses cursor-based pagination for large result sets. The nextPageStartKey field is the cursor.
How it works
First request
Send filter parameters and limit. Do not send nextPageStartKey.
Check the response
If nextPageStartKey is present and not null, more pages exist.
Next page
Send the same request with the nextPageStartKey from the previous response.
Last page
When nextPageStartKey is null or missing, you are done.
Full example
async function getAllTransactions(
baseUrl: string,
token: string,
startDate: string,
endDate: string
) {
const allTransactions: any[] = [];
let nextPageKey: string | undefined;
while (true) {
const payload: Record<string, any> = {
startDate,
endDate,
limit: 100,
};
if (nextPageKey) {
payload.nextPageStartKey = nextPageKey;
}
const queryString = new URLSearchParams(payload).toString();
const response = await fetch(`${baseUrl}/statements/v1/credit-card?${queryString}`, {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
}
});
const data = await response.json();
allTransactions.push(...data.transactions);
nextPageKey = data.nextPageStartKey;
if (!nextPageKey) break;
}
return allTransactions;
}
nextPageStartKey is opaque. Do not decode or modify it — use it exactly as returned.
Rules
| Rule | Value |
|---|---|
| Minimum per page | 5 items |
| Maximum per page | 100 items |
| Maximum range per request | 62 days |
For longer periods, run multiple sequential queries.
Error handling
Error categories
| Code | Meaning | Suggested action |
|---|---|---|
400 | Invalid parameters | Fix parameters and retry |
401 | Token invalid or expired | Refresh the token and retry |
403 | Insufficient permissions | Check your credentials |
404 | Not found | Verify path IDs |
5xx | Server error | Retry with exponential backoff |
Detailed troubleshooting
Causes, examples, and fixes for each error code.
Retry with exponential backoff
For transient errors (5xx, timeout), use automatic retry:
const RETRYABLE_STATUS = new Set([500, 502, 503, 504]);
async function requestWithRetry(
method: string,
url: string,
maxRetries = 3,
options?: RequestInit
): Promise<Response> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 30_000);
const response = await fetch(url, {
method,
...options,
signal: controller.signal,
});
clearTimeout(timeout);
if (!RETRYABLE_STATUS.has(response.status)) {
return response;
}
} catch (err) {
if (!(err instanceof DOMException && err.name === "AbortError")) throw err;
// Treat timeout as retryable
}
if (attempt < maxRetries - 1) {
const waitTime = 2 ** attempt + Math.random();
await new Promise((resolve) => setTimeout(resolve, waitTime * 1000));
}
}
throw new Error("Max retries exceeded");
}
Automatic token refresh
For 401, refresh the token and retry:
async function requestWithAuthRetry(
method: string,
url: string,
options: RequestInit & { headers: Record<string, string> }
): Promise<Response> {
let response = await fetch(url, { method, ...options });
if (response.status === 401) {
invalidateTokenCache();
const newToken = await getFreshToken();
options.headers["Authorization"] = `Bearer ${newToken}`;
response = await fetch(url, { method, ...options });
}
return response;
}
Values and dates
Money amounts
Amounts are represented as decimal numbers in Brazilian reais (BRL):
{
"amountBrl": 150.75
}
Use decimal types in your stack (not binary floats) to avoid rounding errors in financial math.
Dates
| Context | Format | Example |
|---|---|---|
| Request parameters | YYYY-MM-DD | 2025-01-15 |
| Response fields | ISO 8601 (UTC) | 2025-01-15T14:30:00.000Z |
Response timestamps are in UTC. Convert to your app’s local timezone as needed.
International transactions
For international items, exchangeRateUsd carries the FX rate. amountBrl is already in BRL — use exchangeRateUsd only for audit context.
Downloading attachments
Transactions may list receipts in the attachments array. To download them:
- Read the attachment
idfrom the transaction response GET /attachments/v1/content/{attachmentId}— see API Reference- Use the response
Content-Typeto detect format
Supported types
| Content-Type | Extension | Typical use |
|---|---|---|
image/png | .png | Screenshots, photos |
image/jpeg | .jpg | Photos, scanned receipts |
application/pdf | .pdf | Invoices, documents |
Example
import { writeFile, mkdir } from "fs/promises";
import path from "path";
async function downloadAttachment(
baseUrl: string,
token: string,
attachmentId: string,
outputDir = "./downloads"
): Promise<string> {
const response = await fetch(`${baseUrl}/attachments/v1/content/${attachmentId}`, {
headers: { Authorization: `Bearer ${token}` },
});
const contentType = response.headers.get("Content-Type") ?? "";
const extMap: Record<string, string> = {
"image/png": ".png",
"image/jpeg": ".jpg",
"application/pdf": ".pdf",
};
const ext = extMap[contentType] ?? ".bin";
await mkdir(outputDir, { recursive: true });
const filepath = path.join(outputDir, `${attachmentId}${ext}`);
const buffer = Buffer.from(await response.arrayBuffer());
await writeFile(filepath, buffer);
return filepath;
}
Security
Storing credentials
- AWS Secrets Manager
- HashiCorp Vault
- Azure Key Vault
- GCP Secret Manager
- Runtime environment variables (not in code)
- Hardcoding credentials in source code
- Committed
.envfiles - Application logs
- Git repositories, even private
Environment separation
Use different env vars for Sandbox and Production:
# Sandbox
export OPENAPI_BASE_URL=https://api-sandbox.contasimples.com
export OPENAPI_API_KEY={API_KEY_SANDBOX}
# Production
export OPENAPI_BASE_URL=https://api.contasimples.com
export OPENAPI_API_KEY={API_KEY_PROD}
Never use Production credentials in development or test.
Production checklist
- Authentication with automatic token refresh
- Pagination implemented correctly
- Per–HTTP code error handling
- Exponential backoff for 5xx
- Credentials in a secret store
- Sandbox vs Production kept separate
- Monitoring and alerts
- Structured logging with
request_id
Next steps
Last updated today
Built with Documentation.AI