Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/mercadopago/sdk-java/llms.txt

Use this file to discover all available pages before exploring further.

The Mercado Pago Java SDK uses a two-level exception hierarchy to distinguish between problems that occurred before the API could respond and problems reported by the API itself. Understanding which exception you are dealing with — and what data it carries — lets you write precise error-handling logic: retrying transient network blips, surfacing actionable validation errors to users, and alerting on unexpected server faults. This guide covers both exception types in detail, common HTTP status codes, idempotency keys, retry configuration, and pagination with MPSearchRequest.

Exception hierarchy

Exception
└── MPException                        (SDK / transport errors)
    ├── MPJsonParseException           (JSON serialization / deserialization failure)
    ├── MPMalformedRequestException    (invalid request construction)
    └── MPInvalidWebhookSignatureException  (webhook HMAC validation failure — not an API exception)

Exception
└── MPApiException                     (API returned 4xx / 5xx)

MPException — SDK-level errors

MPException (and its subclasses) is thrown when something goes wrong before the API responds or while processing the response — for example, a network timeout, a DNS resolution failure, or a failure to serialize your request object to JSON. These errors are not caused by your request data; they reflect environmental or SDK-internal issues. Key methods inherited from Exception:
MethodDescription
getMessage()Human-readable description of the failure.
getCause()The underlying Throwable that triggered this exception, if any.

MPApiException — API error responses

MPApiException is thrown when the SDK successfully communicates with the Mercado Pago API but the API returns a non-2xx HTTP status code (4xx or 5xx). It always carries the raw API response so you can inspect the error body for details. Key methods:
MethodReturn typeDescription
getStatusCode()intHTTP status code returned by the API (e.g., 400, 401, 404).
getApiResponse()MPResponseFull response object with headers and body.
getApiResponse().getContent()StringRaw JSON body of the error response.
getApiResponse().getStatusCode()IntegerSame status code, accessible from the response object.
getMessage()StringSummary message from the exception.

Catching both exception types

Always catch MPApiException first (it is more specific), then fall back to MPException:
import com.mercadopago.client.payment.PaymentClient;
import com.mercadopago.exceptions.MPApiException;
import com.mercadopago.exceptions.MPException;
import com.mercadopago.resources.payment.Payment;

PaymentClient client = new PaymentClient();

try {
    Payment payment = client.create(createRequest);
    System.out.println("Payment created: " + payment.getId());

} catch (MPApiException e) {
    // The API responded with a 4xx or 5xx status
    int    statusCode    = e.getStatusCode();
    String responseBody  = e.getApiResponse().getContent();

    System.err.println("API error " + statusCode + ": " + responseBody);

} catch (MPException e) {
    // Transport or SDK-internal failure — no API response available
    System.err.println("SDK error: " + e.getMessage());
    if (e.getCause() != null) {
        e.getCause().printStackTrace();
    }
}

Inspecting the API error response

The JSON body in getApiResponse().getContent() typically contains a structured error from Mercado Pago. Parse it with your preferred JSON library to extract the message, error, and cause fields:
import com.mercadopago.exceptions.MPApiException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

try {
    Payment payment = client.create(createRequest);

} catch (MPApiException e) {
    int statusCode = e.getStatusCode();
    String body    = e.getApiResponse().getContent();

    ObjectMapper mapper = new ObjectMapper();
    JsonNode json = mapper.readTree(body);

    System.err.println("Status:  " + statusCode);
    System.err.println("Error:   " + json.path("error").asText());
    System.err.println("Message: " + json.path("message").asText());

    // Detailed causes, when present
    if (json.has("cause")) {
        for (JsonNode cause : json.get("cause")) {
            System.err.println("  Cause code: " + cause.path("code").asText());
            System.err.println("  Cause desc: " + cause.path("description").asText());
        }
    }
}

Common HTTP status codes

The request body failed validation. Inspect the cause array in the response body for field-level error codes and descriptions. Typical causes: missing required fields, invalid data types, amounts below the minimum.
if (e.getStatusCode() == 400) {
    // Parse e.getApiResponse().getContent() for validation details
}
The access token is missing, expired, or does not have the required scopes. Refresh or re-check your token.
if (e.getStatusCode() == 401) {
    // Re-authenticate or check MercadoPagoConfig.setAccessToken(...)
}
The resource (payment, preference, device, payment intent) does not exist or does not belong to the authenticated account.
if (e.getStatusCode() == 404) {
    // The requested resource does not exist
}
A conflicting state prevents the operation — for example, attempting to cancel a payment intent that has already been processed.
if (e.getStatusCode() == 409) {
    // Resolve the conflict before retrying
}
The request is syntactically valid but semantically incorrect for the current state of the resource. Check the cause array for business-rule violations.
Your integration has exceeded the API rate limit. Back off and retry after a delay. Check the Retry-After header when present.
if (e.getStatusCode() == 429) {
    // Exponential back-off before retrying
    Thread.sleep(2000);
}
An unexpected error occurred on Mercado Pago’s side. These are usually transient — retry with exponential back-off. If the problem persists, contact Mercado Pago support and include the request ID from the response headers.

Idempotency keys

POST requests to the Mercado Pago API support idempotency: submitting the same request twice with the same x-idempotency-key returns the result of the first call rather than creating a duplicate resource. The SDK auto-generates a UUID for every POST request, but you can supply your own key via MPRequestOptions when you want deterministic deduplication (e.g., after a timeout retry):
import com.mercadopago.core.MPRequestOptions;
import java.util.Map;
import java.util.UUID;

String idempotencyKey = UUID.randomUUID().toString(); // or derive from your order ID

MPRequestOptions options = MPRequestOptions.builder()
    .customHeaders(Map.of("x-idempotency-key", idempotencyKey))
    .build();

try {
    Payment payment = client.create(createRequest, options);
} catch (MPApiException e) {
    if (e.getStatusCode() >= 500 || e.getStatusCode() == 429) {
        // Safe to retry with the same idempotencyKey
        Payment payment = client.create(createRequest, options);
    }
}
Idempotency keys are scoped to your access token and the specific endpoint. Reusing the same key across different endpoints or different request bodies has no deduplication effect.

Retry strategy

For transient failures (network errors, 5xx responses, 429 rate limits), implement an HTTP-level retry handler using Apache HttpClient’s HttpRequestRetryHandler and register it globally via MercadoPagoConfig:
import com.mercadopago.MercadoPagoConfig;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.protocol.HttpContext;
import java.io.IOException;

HttpRequestRetryHandler retryHandler = new HttpRequestRetryHandler() {
    private static final int MAX_RETRIES = 3;

    @Override
    public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
        if (executionCount > MAX_RETRIES) {
            return false; // exhausted retries
        }
        // Retry on connection resets and socket timeouts
        if (exception instanceof org.apache.http.conn.ConnectTimeoutException) {
            return true;
        }
        if (exception instanceof java.net.SocketTimeoutException) {
            return true;
        }
        if (exception instanceof org.apache.http.NoHttpResponseException) {
            return true;
        }
        return false;
    }
};

MercadoPagoConfig.setRetryHandler(retryHandler);
The HttpRequestRetryHandler operates at the Apache HttpClient transport layer and is invoked only for IOExceptions (network-level failures). API-level errors (MPApiException) — including 429 and 5xx — are not retried by this handler automatically. Implement application-level retry logic for those cases, as shown in the idempotency section above.

Pagination with MPSearchRequest

List and search endpoints use MPSearchRequest to control pagination. Build one with limit and offset and iterate through pages:
import com.mercadopago.net.MPSearchRequest;
import com.mercadopago.client.point.PointClient;
import com.mercadopago.resources.point.PointDevices;
import java.util.Map;

PointClient pointClient = new PointClient();

int pageSize = 50;
int offset   = 0;

while (true) {
    MPSearchRequest searchRequest = MPSearchRequest.builder()
        .limit(pageSize)
        .offset(offset)
        .filters(Map.<String, Object>of("store_id", "987654"))  // optional extra filters
        .build();

    PointDevices page = pointClient.getDevices(searchRequest);

    if (page.getDevices().isEmpty()) {
        break; // no more results
    }

    page.getDevices().forEach(device ->
        System.out.println(device.getId() + " — " + device.getOperatingMode())
    );

    offset += pageSize;

    if (offset >= page.getPaging().getTotal()) {
        break;
    }
}
MPSearchRequest.getParameters() merges limit, offset, and any custom filters into a single query-parameter map, with explicit filter values taking precedence over the dedicated limit/offset fields if they overlap.

MPInvalidWebhookSignatureException

This exception extends MPException but is not related to API communication. It is thrown exclusively by WebhookSignatureValidator.validate(...) when an incoming webhook notification fails HMAC signature verification. It is never thrown during normal PointClient, PaymentClient, or other SDK client calls.
import com.mercadopago.exceptions.MPInvalidWebhookSignatureException;
import com.mercadopago.exceptions.SignatureFailureReason;

try {
    WebhookSignatureValidator.validate(xSignature, xRequestId, dataId, secret);
} catch (MPInvalidWebhookSignatureException e) {
    SignatureFailureReason reason = e.getReason();  // enum — see Webhooks guide
    String requestId             = e.getRequestId();
    String timestamp             = e.getTimestamp();
    // Respond HTTP 401 — do not expose reason externally
}
See the Webhooks guide for the complete list of SignatureFailureReason values and a full endpoint example.

Complete error-handling reference

import com.mercadopago.MercadoPagoConfig;
import com.mercadopago.client.payment.PaymentClient;
import com.mercadopago.core.MPRequestOptions;
import com.mercadopago.exceptions.MPApiException;
import com.mercadopago.exceptions.MPException;
import com.mercadopago.resources.payment.Payment;
import java.util.Map;
import java.util.UUID;

public class PaymentService {

    private final PaymentClient client = new PaymentClient();

    static {
        MercadoPagoConfig.setAccessToken(System.getenv("MP_ACCESS_TOKEN"));
        MercadoPagoConfig.setConnectionTimeout(10000);
        MercadoPagoConfig.setSocketTimeout(15000);
    }

    public Payment createPayment(PaymentCreateRequest createRequest) throws MPException, MPApiException {
        String idempotencyKey = UUID.randomUUID().toString();

        MPRequestOptions options = MPRequestOptions.builder()
            .customHeaders(Map.of("x-idempotency-key", idempotencyKey))
            .build();

        try {
            return client.create(createRequest, options);

        } catch (MPApiException e) {
            switch (e.getStatusCode()) {
                case 400:
                    // Validation error — do not retry, fix the request
                    throw e;
                case 401:
                    // Auth error — check your access token
                    throw e;
                case 404:
                    // Resource not found
                    throw e;
                case 409:
                    // Conflict — check resource state before retrying
                    throw e;
                case 422:
                    // Business rule violation — do not retry
                    throw e;
                case 429:
                    // Rate limited — retry with back-off using the same idempotency key
                    try { Thread.sleep(2000); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); }
                    return client.create(createRequest, options);
                default:
                    if (e.getStatusCode() >= 500) {
                        // Transient server error — retry once with the same idempotency key
                        return client.create(createRequest, options);
                    }
                    throw e;
            }

        } catch (MPException e) {
            // Network or serialization failure — safe to retry (idempotency key reused)
            return client.create(createRequest, options);
        }
    }
}

Build docs developers (and LLMs) love