Gestionarea erorilor
API-ul folosește coduri HTTP standard pentru semnalizarea erorilor. Toate erorile returnează JSON cu descrierea problemei.
Format eroare
{
"statusCode": 400,
"message": "Descrierea erorii",
"error": "Bad Request"
}
Pentru erori de validare, message poate fi un array:
{
"statusCode": 400,
"message": [
"announcementDescription must be at least 40 characters",
"propertyType must be one of: residential, commercial, parking"
],
"error": "Bad Request"
}
Coduri HTTP
| Cod | Tip | Descriere | Reîncercare? |
|---|---|---|---|
| 400 | Bad Request | Parametri cerere incorecți | Nu — corectează datele |
| 401 | Unauthorized | Problemă cu cheia API | Nu — verifică cheia |
| 403 | Forbidden | Fără acces la resursă | Nu — verifică drepturile |
| 404 | Not Found | Resursa nu a fost găsită | Nu — verifică ID-ul |
| 415 | Unsupported Media Type | Content-Type greșit | Nu — folosește application/json |
| 422 | Unprocessable Entity | Toate operațiile bulk au eșuat | Nu — corectează datele |
| 429 | Too Many Requests | Limită cereri depășită | Da — după pauză |
| 500 | Server Error | Eroare internă | Da — cu exponential backoff |
Cerințe pentru cereri
Content-Type
Toate cererile cu corp (POST, PUT, PATCH) trebuie să includă headerul Content-Type: application/json. Cererile cu alte tipuri de conținut primesc răspuns 415 Unsupported Media Type.
Limita dimensiunii corpului
Dimensiunea corpului cererii este limitată la 512 KB. Cererile care depășesc această limită primesc răspuns 413 Payload Too Large.
Rate Limiting
API-ul limitează numărul de cereri pentru protecție contra supraîncărcării. Fiecare răspuns conține headere cu informații despre limite.
| Tip endpoint | Limită |
|---|---|
| Global (toate endpoint-urile) | 300 cereri / minut |
| Endpoint-uri bulk | 30 cereri / minut |
Headere limite
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 295
X-RateLimit-Reset: 1705330800
| Header | Descriere |
|---|---|
X-RateLimit-Limit | Maximum cereri per perioadă |
X-RateLimit-Remaining | Câte cereri au mai rămas |
X-RateLimit-Reset | Când se resetează limita (Unix timestamp) |
Ce să faci la 429
La depășirea limitei, API-ul returnează:
{
"statusCode": 429,
"message": "Too Many Requests"
}
Așteaptă până la timpul din X-RateLimit-Reset și repetă cererea:
async function fetchWithRateLimit(url, options) {
const response = await fetch(url, options);
if (response.status === 429) {
const resetTime = response.headers.get('X-RateLimit-Reset');
const waitMs = (parseInt(resetTime) * 1000) - Date.now();
await new Promise(r => setTimeout(r, Math.max(waitMs, 1000)));
return fetch(url, options); // Repetă
}
return response;
}
Strategie retry
Când să repeți cererile
| Situație | Acțiune |
|---|---|
| Erori 5xx | Repetă cu exponential backoff |
| Erori 429 | Așteaptă până la X-RateLimit-Reset |
| Erori de rețea | Repetă cu backoff |
| Erori 4xx | Nu repeta — corectează cererea |
Exponential backoff
La erori de server, mărește pauza între încercări:
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, options);
// Eroare server — repetă
if (response.status >= 500) {
throw new Error(`Server error: ${response.status}`);
}
return response;
} catch (error) {
if (attempt === maxRetries - 1) throw error;
// Exponential backoff: 1s, 2s, 4s
const delay = 1000 * Math.pow(2, attempt);
await new Promise(r => setTimeout(r, delay));
}
}
}
// Utilizare
const response = await fetchWithRetry(
'https://crm.rentix.md/api/v1/listings',
{
method: 'PUT',
headers: { 'Authorization': 'ApiKey YOUR_API_KEY' },
body: JSON.stringify(data)
}
);
function fetchWithRetry($url, $options, $maxRetries = 3) {
for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
$ch = curl_init($url);
curl_setopt_array($ch, $options);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode < 500) {
return ['code' => $httpCode, 'body' => $response];
}
if ($attempt < $maxRetries - 1) {
// Exponential backoff: 1s, 2s, 4s
sleep(pow(2, $attempt));
}
}
throw new Exception("Max retries exceeded");
}
Erori tipice
Autentificare (400, 401, 403)
| Eroare | Cauză | Soluție |
|---|---|---|
Multiple authentication methods provided | Au fost trimise ambele headere Authorization și X-API-Key | Folosește doar un header de autentificare |
API key is required | Cheia API nu a fost furnizată | Adaugă headerul Authorization: ApiKey YOUR_KEY |
Invalid API key | Cheia API este incorectă sau a fost revocată | Verifică valoarea cheii |
Agency is suspended | Contul agenției este suspendat | Contactează suportul |
CRM is not enabled for this agency | Accesul CRM nu este activat | Contactează suportul pentru activarea CRM |
Anunțuri (400)
| Eroare | Cauză | Soluție |
|---|---|---|
Cannot provide both id and externalId | Ai transmis ambii identificatori | Folosește unul singur |
Must provide id or externalId | Nu ai transmis identificator pentru actualizare | Adaugă id sau externalId |
External ID already linked | External ID este ocupat de alt anunț | Folosește un ID unic |
Minimum 3 photos required | Pentru publicare sunt necesare fotografii | Încarcă minim 3 fotografii |
Description too short | Descrierea are mai puțin de 40 caractere | Adaugă mai mult text |
Fișiere media (400)
| Eroare | Cauză | Soluție |
|---|---|---|
Duplicate external ID | External ID este deja folosit | Folosește un ID unic |
File not found | Fișierul nu există | Verifică fileId sau externalFileId |
Invalid file type | Format nesuportat | Folosește JPEG, PNG, WebP sau HEIC |
Upload URL expired | Signed URL a expirat (30 minute) | Solicită un URL nou |
Validare câmpuri
| Cod | Descriere |
|---|---|
STRING_TOO_SHORT | Stringul e mai scurt decât minimul |
STRING_TOO_LONG | Stringul e mai lung decât maximul |
NUMBER_TOO_SMALL | Numărul e mai mic decât minimul |
NUMBER_TOO_LARGE | Numărul e mai mare decât maximul |
INVALID_ENUM_VALUE | Valoarea nu e din lista permisă |
REQUIRED_FIELD | Câmpul obligatoriu nu e completat |
Exemplu: gestionarea tuturor cazurilor
async function apiRequest(endpoint, data) {
const url = `https://crm.rentix.md/api/v1${endpoint}`;
const options = {
method: 'PUT',
headers: {
'Authorization': 'ApiKey YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
};
for (let attempt = 0; attempt < 3; attempt++) {
const response = await fetch(url, options);
// Succes
if (response.ok) {
return response.json();
}
// Rate limit — așteptăm și repetăm
if (response.status === 429) {
const resetTime = response.headers.get('X-RateLimit-Reset');
const waitMs = (parseInt(resetTime) * 1000) - Date.now();
await new Promise(r => setTimeout(r, Math.max(waitMs, 1000)));
continue;
}
// Eroare server — repetăm cu backoff
if (response.status >= 500) {
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
continue;
}
// Eroare client — nu repetăm
const error = await response.json();
throw new Error(`API Error: ${JSON.stringify(error.message)}`);
}
throw new Error('Max retries exceeded');
}
Recomandări
- Logează erorile — salvează
statusCode,messageși timestamp pentru diagnosticare - Nu ignora 4xx — sunt erori în datele tale, corectează-le
- Respectă rate limits — la 429 așteaptă întotdeauna timpul indicat
- Folosește exponential backoff — la 5xx nu bombarda serverul imediat