1. U4 ERPx
  2. API Reference
  3. ObjectAPI
  4. Response headers

Response headers

Response headers that might provide additional information

In addition to standard response headers, several custom headers are also returned. Not all of them have high relevance; some are present as telemetry data. The informative headers are described below.

Important response headers

X-U4-RemainingLimit

Contains the maximum number of calls that can be made in the current minute (sliding window) time-frame with the current subscription claim given by the X-U4-SubscriptionClaim header.

X-U4-QuotaType

As described in the limits section, APIs have separate inbound and outbound quotas (per 5 minutes and per 24 hours). This header provides information about which quota was affected by this call: INBOUND or OUTBOUND.

Retry-After

This is a standard response header, but due to its importance, we mention it here as well. When a response with status code 429 is returned, this header provides information regarding the number of seconds one should wait before the next attempt to make a call. The same information is also available in the response body, but this header can be used for applying automatic retry mechanisms in the caller code. Example: Retry-After: 32.

The following C# example demonstrates how to set up automatic retry behaviour based on the Retry-After header:

// filepath: Example retry policy implementation
using Microsoft.Extensions.DependencyInjection;
using Polly;
using Polly.Extensions.Http;
using System.Net.Http.Headers;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpClient("RetryClient")
    .AddPolicyHandler(GetRetryPolicy());

static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.TooManyRequests || 
                        msg.StatusCode == System.Net.HttpStatusCode.ServiceUnavailable)
        .WaitAndRetryAsync(3, (retryAttempt, response, context) =>
        {
            var randomBuffer = TimeSpan.FromMilliseconds(500 + (Random.Shared.NextDouble() * 500)); // 500ms to 1000ms

            if (response.Result?.Headers?.RetryAfter != null)
            {
                var retryAfter = response.Result.Headers.RetryAfter;
                if (retryAfter.Delta.HasValue)
                {
                    return retryAfter.Delta.Value + randomBuffer;
                }
                else if (retryAfter.Date.HasValue)
                {
                    var delay = retryAfter.Date.Value - DateTimeOffset.UtcNow + randomBuffer;
                    return delay > TimeSpan.Zero ? delay : randomBuffer;
                }
            }
            return TimeSpan.FromSeconds(2); // fallback
        });
}