Skip to content

API Polling

Patterns for polling external APIs with TickerQ.

Poll External API Periodically

Poll an external API every 5 minutes:

csharp
[TickerFunction("PollExternalApi", cronExpression: "0 */5 * * * *")]
public async Task PollExternalApi(
    TickerFunctionContext context,
    CancellationToken cancellationToken)
{
    using var scope = context.ServiceScope.ServiceProvider.CreateScope();
    var apiClient = scope.ServiceProvider.GetRequiredService<IExternalApiClient>();
    var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
    
    try
    {
        var data = await apiClient.FetchDataAsync(cancellationToken);
        
        // Process and store data
        foreach (var item in data)
        {
            var existing = await dbContext.DataItems
                .FirstOrDefaultAsync(d => d.ExternalId == item.Id, cancellationToken);
            
            if (existing == null)
            {
                dbContext.DataItems.Add(new DataItem { /* ... */ });
            }
            else
            {
                // Update existing
                existing.Value = item.Value;
            }
        }
        
        await dbContext.SaveChangesAsync(cancellationToken);
    }
    catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
    {
        _logger.LogWarning("Rate limited, will retry on next occurrence");
        // Don't throw - job will retry on next scheduled occurrence
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Failed to poll external API");
        throw; // Will trigger retry if configured
    }
}

Poll with Backoff on Failure

Implement exponential backoff based on retry count:

csharp
[TickerFunction("PollWithBackoff")]
public async Task PollWithBackoff(
    TickerFunctionContext context,
    CancellationToken cancellationToken)
{
    var apiClient = _serviceProvider.GetRequiredService<IExternalApiClient>();
    
    // Use retry count to implement exponential backoff
    var retryCount = context.RetryCount;
    var delaySeconds = (int)Math.Pow(2, retryCount); // 1s, 2s, 4s, 8s...
    
    if (retryCount > 0)
    {
        _logger.LogInformation("Retrying after {Delay}s (attempt {Attempt})", 
            delaySeconds, retryCount + 1);
        await Task.Delay(TimeSpan.FromSeconds(delaySeconds), cancellationToken);
    }
    
    try
    {
        var data = await apiClient.FetchDataAsync(cancellationToken);
        await ProcessDataAsync(data, cancellationToken);
    }
    catch (Exception ex) when (retryCount < 5)
    {
        _logger.LogWarning(ex, "API call failed, will retry");
        throw; // Trigger retry
    }
}

Poll Multiple Endpoints

Poll multiple APIs in sequence:

csharp
[TickerFunction("PollMultipleApis", cronExpression: "0 */10 * * * *")]
public async Task PollMultipleApis(
    TickerFunctionContext context,
    CancellationToken cancellationToken)
{
    var endpoints = new[] { "api1", "api2", "api3" };
    
    foreach (var endpoint in endpoints)
    {
        try
        {
            await PollEndpointAsync(endpoint, cancellationToken);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to poll {Endpoint}", endpoint);
            // Continue with next endpoint instead of failing entire job
        }
    }
}

private async Task PollEndpointAsync(string endpoint, CancellationToken cancellationToken)
{
    // Poll logic here
}

See Also

Built by Albert Kunushevci