What error codes can I expect from Merge and how do I handle them?

Last updated: July 15, 2025

Error handling

Handling error messages based on different response codes is critical to API development. Here’s a general approach to handling various HTTP response codes coming back from Merge effectively.

Categorize response codes

HTTP error codes can be categorized into different classes. Only some are used by Merge.

  • 3xx: Redirection messages (not returned by Merge)

  • 4xx: Client errors

  • 5xx: Server errors

Define error handling logic

Here are the types of errors returned by Merge and guidance on each:

Note, the "Retry" column applies to POSTs. GET requests are always safe to retry with a maximum retry threshold.

4xx: Client errors

Code

Issue

Description

Retry

Troubleshooting Tips

400

Bad Request

Malformed request- data is not present in the 3rd Party System and/or the 3rd party system does not support the request.

No

Check if the 3rd party system has the data you want to pull or push to.

Check our docs to see what endpoints we support for each integration.

401

Unauthorized

Unauthorized: authorization is either missing or misconfigured.

No

If this is a test account, make sure you are using the Account Token stored in the bottom right of their Linked Account page.

If this is a production account, the account token are available in the dashboard and should be stored in your database.

404

URL Not Found

The resource was not found with the given identifier- the request was entered incorrectly.

No

Check our docs on how to format our requests to the Merge API.

404

URL Not Found

The resource was not found with the given identifier- the data does not exist.

No

Check if the data is present within the 3rd party system.

404

URL Not Found

The resource was not found with the given identifier- the integration does not support that endpoint.

No

Check our docs to see what common models our integrations support.

404

URL Not Found

The resource was not found with the given identifier- the subdomain was entered incorrectly.

No

Check for a valid subdomain. If it is accurate, contact support.

408

Passthrough request timeout

The passthrough request to the third party exceeded the 60 second timeout limit

No

Use async passthrough.

429

Too Many Requests

The Merge Organization has reached a rate limit for the requested linked account, preventing any more requests from going through.

Yes

Retry using the logic explained in the Automatic Retry section below.

5xx: Server errors

Code

Issue

Description

Retry

Troubleshooting Tips

500

Internal Server Error

Merge is experiencing a service interruption.

Yes*

Retry using the logic explained in the Automatic Retry section below.

502, 503, 504

Service Interruption

The connection was interrupted or timed out.

Yes*

Retry using the logic explained in the Automatic Retry section below.

Automatic retry

It's generally best practice to have pre-defined retry logic when encountering specific error codes. Below is guidance on what logic we recommend setting for each response code you get back from Merge.

Automatic retry logic

Use the retry logic outlined below to ensure that transient issues do not cause immediate failures, improving the resilience of your integration.

Code

Issue

Description

Retry Logic

429

Too Many Requests

The Merge Organization has reached a rate limit for the requested linked account, preventing any more requests from going through.

Retry the request after an initial 1 minute delay, increasing the delay exponentially with each retry (aka "exponential backoff")

500

Internal Server Error

Merge is experiencing a service interruption.

Retry the request after an initial 5-30s delay, increasing the delay exponentially with each retry (aka "exponential backoff")

POST requests must use the Idempotency-Key header to avoid creating duplicate records

502, 503, 504

Service Interruption

The connection was interrupted or timed out.

Retry the request after an initial 5-30s delay, increasing the delay exponentially with each retry (aka "exponential backoff")

POST requests must use the Idempotency-Key header to avoid creating duplicate records

What is "exponential backoff"?

An exponential backoff algorithm reduces the rate of API requests in response to an error code. For example, if an API request hits a 5xx error, it might try again 1 second later, then if it fails again, 2 seconds later, then 4 seconds, etc. Each time, the pause and subsequent attempt are multiplied by a fixed amount (in this case, 2).

Example of retry logic

GET request

An example of retry logic with exponential backoff for a GET request using our Node SDK is included below.

async function listFiles(modifiedAfter: Date) {
  const { accountToken, apiKey } = retrieveTokens()
  const merge = new MergeClient({ apiKey, accountToken });
  let pageSize = 100; // default limit
  let originalPageSize = pageSize; // store the original page size
  let cursor: string | undefined;
  const files: any[] = [];
  const maxRetries = 3;
  let retryCount = 0;
  let retryDelay = 5000; // initial delay of 5 seconds

  while (true) {
    try {
      const response = await merge.filestorage.files.list({
        modifiedAfter,
        pageSize,
        cursor,
      });

      if (response.results) {
        files.push(...response.results);
      }
      cursor = response.next;

      // Reset retry count, delay, and pageSize after a successful request
      retryCount = 0;
      retryDelay = 1000;
      pageSize = originalPageSize;

      if (!cursor) {
        break;
      }
    } catch (error) {
      if (error.response.status === 429) {
        // Too Many Requests, sleep for 60 seconds and retry
        await new Promise(resolve => setTimeout(resolve, 60000));
      } else {
        // Server Error or other errors, retry with backoff
        retryCount++;
        if (retryCount <= maxRetries) {
          await new Promise(resolve => setTimeout(resolve, retryDelay));
          retryDelay *= 2; // exponential backoff
          if (error.response.status >= 500 && error.response.status < 600) {
            pageSize = Math.floor(pageSize / 2);
          }
        } else {
          throw error;
        }
      }
    }
  }

  return files;
}

POST request

An example of retry logic with exponential backoff for a POST request using our Node SDK is included below.

async function createInvoice(invoice: InvoiceRequest): Promise<InvoiceResponse> {
  const { accountToken, apiKey } = retrieveTokens();
  const merge = new MergeClient({ apiKey, accountToken });
  const maxRetries = 3;
  let retryCount = 0;
  let retryDelay = 5000; // initial delay of 5 seconds

  while (true) {
    try {
      const response = await merge.accounting.invoices.create({
        model: invoice
      });
      return response;
    } catch (error) {
      console.error(`Error creating invoice: ${error.message}`);

      if (error.response.status === 429) {
        // Too Many Requests, sleep for 60 seconds and retry
        await new Promise(resolve => setTimeout(resolve, 60000));
      } else if (error.response.status >= 500 && error.response.status < 600) {
        // Server Error, retry with backoff
        retryCount++;
        if (retryCount <= maxRetries) {
          await new Promise(resolve => setTimeout(resolve, retryDelay));
          retryDelay *= 2; // exponential backoff
        } else {
          throw error;
        }
      } else {
        throw error;
      }
    }
  }
}