How to Poll for API Request Status in .NET with C#

Use .NET with C# to request asynchronous pdfRest processing and poll the returned request ID to a terminal status.
Share this page

In this tutorial, we'll use C# and the API Polling Tool to submit asynchronous document work and poll its request status. The complete Poll for API Request Status repository sample stays visible while we explain the request fields and response behavior a C# application must adapt safely.

Why Poll for API Request Status in .NET with C#?

Background services and web applications benefit from decoupling document submission from completion. API Polling gives the .NET caller a request ID immediately and exposes the eventual processing response through a GET request.

An ASP.NET job coordinator might begin a BMP rendering operation, persist the identifier, and let a hosted worker check it every few seconds. The web request can finish quickly while the worker owns timeout, retry, and completion behavior.

The C# sample isolates initial submission and status retrieval in separate methods. That separation makes the two contracts clear: the first POST asks for requestId, while each later GET authenticates normally and addresses that identifier in the route.

C# Code Example

/*
 * What this sample does:
 * - Demonstrates polling with response-type header and request-status.
 * - Routed from Program.cs as: `dotnet run -- request-status-multipart `.
 *
 * Setup (environment):
 * - Copy .env.example to .env
 * - Set PDFREST_API_KEY=your_api_key_here
 * - Optional: set PDFREST_URL to override the API region. For EU/GDPR compliance and proximity, use:
 *     PDFREST_URL=https://eu-api.pdfrest.com
 *   For more information visit https://pdfrest.com/pricing#how-do-eu-gdpr-api-calls-work
 *
 * Usage:
 *   dotnet run -- request-status-multipart /path/to/input.pdf
 *
 * Output:
 * - Prints interim responses and final JSON; validation errors exit non-zero.
 */

using System.Text;
using Newtonsoft.Json.Linq;

namespace Samples.EndpointExamples.MultipartPayload
{
    public static class RequestStatus
    {
        public static async Task Execute(string[] args)
        {
            if (args == null || args.Length < 1)
            {
                Console.Error.WriteLine("request-status-multipart requires ");
                Environment.Exit(1);
                return;
            }
            var inputPath = args[0];
            if (!File.Exists(inputPath))
            {
                Console.Error.WriteLine($"File not found: {inputPath}");
                Environment.Exit(1);
                return;
            }

            var apiKey = Environment.GetEnvironmentVariable("PDFREST_API_KEY");
            if (string.IsNullOrWhiteSpace(apiKey))
            {
                Console.Error.WriteLine("Missing required environment variable: PDFREST_API_KEY");
                Environment.Exit(1);
                return;
            }
            var baseUrl = Environment.GetEnvironmentVariable("PDFREST_URL") ?? "https://api.pdfrest.com";

            // Send initial request to an arbitrary route (bmp) with response-type header to get requestId
            string bmpResponse = await GetBmpResponseAsync(baseUrl, apiKey, inputPath, Path.GetFileName(inputPath));
            dynamic bmpJson = JObject.Parse(bmpResponse);
            if (bmpJson.ContainsKey("error"))
            {
                Console.Error.WriteLine($"Error from initial request: {bmpJson.error}");
                Environment.Exit(1);
                return;
            }
            string requestId = bmpJson.requestId;
            Console.WriteLine($"Received request ID: {requestId}");

            // Poll request-status until not pending
            string statusResponse = await GetRequestStatusAsync(baseUrl, apiKey, requestId);
            dynamic statusJson = JObject.Parse(statusResponse);
            while (statusJson.status == "pending")
            {
                const int delay = 5;
                Console.WriteLine($"Response from /request-status for request {requestId}: {statusJson}");
                Console.WriteLine($"Request status was \"pending\". Checking again in {delay} seconds...");
                await Task.Delay(TimeSpan.FromSeconds(delay));
                statusResponse = await GetRequestStatusAsync(baseUrl, apiKey, requestId);
                statusJson = JObject.Parse(statusResponse);
            }
            Console.WriteLine($"Response from /request-status: {statusJson}");
            Console.WriteLine("Done!");
        }

        private static async Task GetBmpResponseAsync(string baseUrl, string apiKey, string pathToFile, string fileName)
        {
            using var httpClient = new HttpClient { BaseAddress = new Uri(baseUrl) };
            using var bmpRequest = new HttpRequestMessage(HttpMethod.Post, "bmp");
            bmpRequest.Headers.TryAddWithoutValidation("Api-Key", apiKey);
            bmpRequest.Headers.Accept.Add(new("application/json"));
            var multipartContent = new MultipartFormDataContent();
            var byteArray = File.ReadAllBytes(pathToFile);
            var byteAryContent = new ByteArrayContent(byteArray);
            multipartContent.Add(byteAryContent, "file", fileName);
            byteAryContent.Headers.TryAddWithoutValidation("Content-Type", "application/octet-stream");
            bmpRequest.Headers.Add("response-type", "requestId");
            bmpRequest.Content = multipartContent;
            var bmpResponse = await httpClient.SendAsync(bmpRequest);
            return await bmpResponse.Content.ReadAsStringAsync();
        }

        private static async Task GetRequestStatusAsync(string baseUrl, string apiKey, string requestId)
        {
            using var httpClient = new HttpClient { BaseAddress = new Uri(baseUrl) };
            using var request = new HttpRequestMessage(HttpMethod.Get, $"request-status/{requestId}");
            request.Headers.TryAddWithoutValidation("Api-Key", apiKey);
            var response = await httpClient.SendAsync(request);
            return await response.Content.ReadAsStringAsync();
        }
    }
}

Source for Poll for API Request Status: View the C# sample on GitHub.

Breaking Down the Code

Load the .NET request and JSON types

using System.Text;
using Newtonsoft.Json.Linq;

The Poll for API Request Status sample uses .NET HTTP types for requests and Newtonsoft.Json.Linq for response parsing. Its supporting namespaces cover the text and multipart values needed by this C# Poll for API Request Status workflow.

Validate input and load runtime configuration

Console.Error.WriteLine($"File not found: {inputPath}");
                Environment.Exit(1);
                return;
            }

            var apiKey = Environment.GetEnvironmentVariable("PDFREST_API_KEY");
            if (string.IsNullOrWhiteSpace(apiKey))
            {
                Console.Error.WriteLine("Missing required environment variable: PDFREST_API_KEY");
                Environment.Exit(1);
                return;
            }
            var baseUrl = Environment.GetEnvironmentVariable("PDFREST_URL") ?? "https://api.pdfrest.com";

            // Send initial request to an arbitrary route (bmp) with response-type header to get requestId

The .NET Poll for API Request Status example reads PDFREST_API_KEY and optionally PDFREST_URL from the environment. That arrangement keeps C# Poll for API Request Status credentials out of source and permits US or EU routing without recompilation.

Submit work and retain the request ID

// Send initial request to an arbitrary route (bmp) with response-type header to get requestId
            string bmpResponse = await GetBmpResponseAsync(baseUrl, apiKey, inputPath, Path.GetFileName(inputPath));
            dynamic bmpJson = JObject.Parse(bmpResponse);
            if (bmpJson.ContainsKey("error"))
            {
                Console.Error.WriteLine($"Error from initial request: {bmpJson.error}");
                Environment.Exit(1);
                return;
            }
            string requestId = bmpJson.requestId;
            Console.WriteLine($"Received request ID: {requestId}");

Adding response-type: requestId changes the initial C# processing response into an asynchronous identifier. The Poll for API Request Status operation continues normally while the C# caller prepares its status checks.

Poll until the status changes

// Poll request-status until not pending
            string statusResponse = await GetRequestStatusAsync(baseUrl, apiKey, requestId);
            dynamic statusJson = JObject.Parse(statusResponse);
            while (statusJson.status == "pending")
            {
                const int delay = 5;
                Console.WriteLine($"Response from /request-status for request {requestId}: {statusJson}");
                Console.WriteLine($"Request status was \"pending\". Checking again in {delay} seconds...");
                await Task.Delay(TimeSpan.FromSeconds(delay));
                statusResponse = await GetRequestStatusAsync(baseUrl, apiKey, requestId);
                statusJson = JObject.Parse(statusResponse);
            }
            Console.WriteLine($"Response from /request-status: {statusJson}");
            Console.WriteLine("Done!");

The C# status request addresses /request-status/{requestId} and repeats while the state is pending. A production C# Poll for API Request Status implementation should impose a deadline and handle every terminal error explicitly.

Define the initial asynchronous request

private static async Task GetBmpResponseAsync(string baseUrl, string apiKey, string pathToFile, string fileName)
        {
            using var httpClient = new HttpClient { BaseAddress = new Uri(baseUrl) };
            using var bmpRequest = new HttpRequestMessage(HttpMethod.Post, "bmp");
            bmpRequest.Headers.TryAddWithoutValidation("Api-Key", apiKey);
            bmpRequest.Headers.Accept.Add(new("application/json"));
            var multipartContent = new MultipartFormDataContent();
            var byteArray = File.ReadAllBytes(pathToFile);
            var byteAryContent = new ByteArrayContent(byteArray);
            multipartContent.Add(byteAryContent, "file", fileName);
            byteAryContent.Headers.TryAddWithoutValidation("Content-Type", "application/octet-stream");
            bmpRequest.Headers.Add("response-type", "requestId");
            bmpRequest.Content = multipartContent;
            var bmpResponse = await httpClient.SendAsync(bmpRequest);
            return await bmpResponse.Content.ReadAsStringAsync();
        }

The C# request joins the Poll for API Request Status endpoint, authentication header, and prepared body. For this Poll for API Request Status call, the multipart metadata generated by C# must remain paired with the body so pdfRest can separate files from options.

Define the status lookup request

private static async Task GetRequestStatusAsync(string baseUrl, string apiKey, string requestId)
        {
            using var httpClient = new HttpClient { BaseAddress = new Uri(baseUrl) };
            using var request = new HttpRequestMessage(HttpMethod.Get, $"request-status/{requestId}");
            request.Headers.TryAddWithoutValidation("Api-Key", apiKey);
            var response = await httpClient.SendAsync(request);
            return await response.Content.ReadAsStringAsync();
        }
    }

The final Poll for API Request Status handling in C# exposes the HTTP result and JSON body. Before using a Poll for API Request Status output or request ID, production C# code should verify the status and retain the identifier required by the next step.

Beyond the Tutorial

You can now separate pdfRest submission from completion in C#, retain the returned request ID, and retrieve the terminal response on your own schedule.

Treat the five-second delay as a configurable policy and cap the total polling duration. Record terminal error responses with the original input so operators can distinguish failed processing from network interruption or an expired job.

Try the Poll for API Request Status workflow with a representative file in API Lab, then adapt the request in C#. The API Polling Tool documentation provides the complete C# context for Poll for API Request Status values, defaults, and limitations.

Generate a self-service API Key now!
Create your FREE API Key to start processing PDFs in seconds, only possible with pdfRest.