Error handling & rate limits

DigiCert® ONE APIs return JSON errors and enforces per-API key rate limits. Understanding returned errors and rate limits helps you:

  • Parse error payloads and map them to actionable logic.
  • Respect rate limits to avoid throttling.
  • Implement production-grade retry and logging strategies.

Error response structure and example

Error response structure

{
  "error": {
    "code": "<error_code>",
    "message": "<error_message>"
  }
}
  • code: Returned error code.
  • message: Description of the error that occurred and potential solutions.

Error response example

{
  "errors": [
    {
      "code": "duplicate_error",
      "message": "Nickname api_service_user_3 already exists. Enter a different nickname."
    }
  ]
}

API troubleshooting basics

SymptomLikely causeQuick fix
401 UnauthorizedMissing/invalid x-api-key or expired certificateVerify the x-api-key header; confirm the service user is active.
403 ForbiddenAuthenticated but lacks required roleAdd the needed role in Account Manager or use a different key.
429 Too Many RequestsBurst trafficImplement backoff; spread calls across service users.
503 Service UnavailableMaintenance windowRetry after header; escalate if >10 min.
SSL handshake failure when using mTLSWrong certificate, key mismatch, or incorrect URLCheck cert/key pair and ensure URL prefix is correct.
Everything stopped at midnightKey or service user reached end dateCreate/rotate the key before expiry; update all integrations.
Works in demo, fails in prodUsing demo key against prod hostnameGenerate a new key in production; update DC_BASE environment variable.

HTTP status and machine codes

HTTPMachine codeMeaningRetry?
200 OKSuccess
201 CreatedResource created
204 No ContentSuccess, no body
400 Bad Requestinvalid_requestPayload failed validationNo
401 Unauthorizedinvalid_api_keyBad or missing API keyNo
403 Forbiddenaccess_deniedAuthenticated but not authorisedNo
404 Not Foundresource_not_foundResource ID or path is wrongNo
409 ConflictconflictResource already exists or version clashNo
429 Too Many Requestsrate_limit_exceededRate limit hitAfter Retry-After
500 Internal Server Errorinternal_errorUnexpected server faultWith backoff
503 Service Unavailableservice_unavailableMaintenance or downstream outageAfter Retry-After

Sample retry logic

import time
import requests
from typing import Dict, Any

def api_request(url: str, headers: Dict[str, str], max_retries: int = 3) -> Any:
    """Simple retry with exponential backoff for API requests."""
    for i in range(max_retries):
        try:
            response = requests.get(url, headers=headers)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as error:
            # Check if error is retryable
            status_code = getattr(error.response, 'status_code', 0)
            is_retryable = status_code in [429, 500, 503]
            is_last_attempt = i == max_retries - 1
            
            if not is_retryable or is_last_attempt:
                raise
            
            # Wait before retrying: 1s, 2s, 4s...
            delay = 2 ** i
            time.sleep(delay)

# Example: Get certificates from DigiCert
certificates = api_request(
    'https://api.digicert.com/v1/certificates',
    headers={'X-API-Key': os.environ['DIGICERT_API_KEY']}
)
import java.io.IOException;
import java.net.http.*;
import java.time.Duration;

public class ApiClient {
    private static final HttpClient client = HttpClient.newHttpClient();
    
    public static String apiRequest(String url, String apiKey, int maxRetries) 
            throws IOException, InterruptedException {
        
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .header("X-API-Key", apiKey)
            .header("Content-Type", "application/json")
            .GET()
            .build();
        
        for (int i = 0; i < maxRetries; i++) {
            try {
                HttpResponse<String> response = client.send(request, 
                    HttpResponse.BodyHandlers.ofString());
                
                if (response.statusCode() >= 200 && response.statusCode() < 300) {
                    return response.body();
                }
                
                // Check if error is retryable
                int status = response.statusCode();
                boolean isRetryable = status == 429 || status == 500 || status == 503;
                boolean isLastAttempt = i == maxRetries - 1;
                
                if (!isRetryable || isLastAttempt) {
                    throw new IOException("API request failed: " + status);
                }
                
            } catch (IOException | InterruptedException e) {
                if (i == maxRetries - 1) throw e;
            }
            
            // Wait before retrying: 1s, 2s, 4s...
            Thread.sleep((long) Math.pow(2, i) * 1000);
        }
        
        throw new IOException("Max retries exceeded");
    }
    
    // Example: Get certificates from DigiCert
    public static void main(String[] args) throws Exception {
        String response = apiRequest(
            "https://api.digicert.com/v1/certificates",
            System.getenv("DIGICERT_API_KEY"),
            3
        );
        System.out.println(response);
    }
}
package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "time"
)


// apiRequest makes an HTTP GET request with exponential backoff retry
func apiRequest(url string, headers map[string]string, maxRetries int) ([]byte, error) {
    client := &http.Client{Timeout: 30 * time.Second}
    
    for i := 0; i < maxRetries; i++ {
        req, err := http.NewRequest("GET", url, nil)
        if err != nil {
            return nil, err
        }
        
        // Add headers
        for key, value := range headers {
            req.Header.Set(key, value)
        }
        
        resp, err := client.Do(req)
        if err != nil {
            if i == maxRetries-1 {
                return nil, err
            }
            time.Sleep(time.Duration(1<<i) * time.Second)
            continue
        }
        defer resp.Body.Close()
        
        // Check if error is retryable
        isRetryable := resp.StatusCode == 429 || 
                      resp.StatusCode == 500 || 
                      resp.StatusCode == 503
        isLastAttempt := i == maxRetries-1
        
        if resp.StatusCode >= 200 && resp.StatusCode < 300 {
            return io.ReadAll(resp.Body)
        }
        
        if !isRetryable || isLastAttempt {
            return nil, fmt.Errorf("API request failed: %d", resp.StatusCode)
        }
        
        // Wait before retrying: 1s, 2s, 4s...
        time.Sleep(time.Duration(1<<i) * time.Second)
    }
    
    return nil, fmt.Errorf("max retries exceeded")
}

// Example: Get certificates from DigiCert
func main() {
    headers := map[string]string{
        "X-API-Key": os.Getenv("DIGICERT_API_KEY"),
        "Content-Type": "application/json",
    }
    
    data, err := apiRequest(
        "https://api.digicert.com/v1/certificates",
        headers,
        3,
    )
    if err != nil {
        panic(err)
    }
    
    fmt.Println(string(data))
}
using System;
using System.Net.Http;
using System.Threading.Tasks;

public class ApiClient
{
    private static readonly HttpClient client = new HttpClient();
    private static readonly int[] retryableStatusCodes = { 429, 500, 503 };
    
    public static async Task<string> ApiRequest(string url, string apiKey, int maxRetries = 3)
    {
        client.DefaultRequestHeaders.Clear();
        client.DefaultRequestHeaders.Add("X-API-Key", apiKey);
        
        for (int i = 0; i < maxRetries; i++)
        {
            try
            {
                var response = await client.GetAsync(url);
                
                if (response.IsSuccessStatusCode)
                {
                    return await response.Content.ReadAsStringAsync();
                }
                
                // Check if error is retryable
                var statusCode = (int)response.StatusCode;
                var isRetryable = Array.Exists(retryableStatusCodes, 
                    code => code == statusCode);
                var isLastAttempt = i == maxRetries - 1;
                
                if (!isRetryable || isLastAttempt)
                {
                    response.EnsureSuccessStatusCode();
                }
            }
            catch (HttpRequestException ex)
            {
                if (i == maxRetries - 1) throw;
            }
            
            // Wait before retrying: 1s, 2s, 4s...
            await Task.Delay((int)Math.Pow(2, i) * 1000);
        }
        
        throw new Exception("Max retries exceeded");
    }
    
    // Example: Get certificates from DigiCert
    static async Task Main()
    {
        var apiKey = Environment.GetEnvironmentVariable("DIGICERT_API_KEY");
        var result = await ApiRequest(
            "https://api.digicert.com/v1/certificates",
            apiKey
        );
        Console.WriteLine(result);
    }
}
function Invoke-ApiRequest {
    param(
        [string]$Url,
        [hashtable]$Headers,
        [int]$MaxRetries = 3
    )
    
    $retryableStatusCodes = @(429, 500, 503)
    
    for ($i = 0; $i -lt $MaxRetries; $i++) {
        try {
            $response = Invoke-RestMethod -Uri $Url -Headers $Headers -Method Get
            return $response
        }
        catch {
            $statusCode = $_.Exception.Response.StatusCode.value__
            $isRetryable = $statusCode -in $retryableStatusCodes
            $isLastAttempt = $i -eq ($MaxRetries - 1)
            
            if (-not $isRetryable -or $isLastAttempt) {
                throw $_
            }
            
            # Wait before retrying: 1s, 2s, 4s...
            $delay = [math]::Pow(2, $i)
            Write-Host "Retrying in $delay seconds..."
            Start-Sleep -Seconds $delay
        }
    }
}

# Example: Get certificates from DigiCert
$headers = @{
    'X-API-Key' = $env:DIGICERT_API_KEY
    'Content-Type' = 'application/json'
}

$certificates = Invoke-ApiRequest `
    -Url 'https://api.digicert.com/v1/certificates' `
    -Headers $headers `
    -MaxRetries 3

$certificates | ConvertTo-Json