← Back to Blog

BIN Lookup API for C#: Integration Guide

Published: February 5, 2026
Tags: csharp, dotnet, tutorial, integration, developer, enterprise

This guide walks you through integrating BINLookupAPI into your C#/.NET application, from creating your account to writing production-ready code with proper error handling and modern .NET patterns.

What You'll Build
  • Look up BIN information for any payment card
  • Handle all error cases gracefully
  • Work with async/await patterns throughout
  • Validate cards in a real payment flow

Prerequisites

  • .NET 6.0 or higher
  • Visual Studio 2022, VS Code, or JetBrains Rider
  • Basic familiarity with async/await in C#

No additional NuGet packages are required for basic usage. The built-in System.Net.Http and System.Text.Json namespaces provide everything you need.

# Create a new console project to follow along
dotnet new console -n BinLookupDemo
cd BinLookupDemo

Step 1: Create Your Account

First, you’ll need a BINLookupAPI account to get your API key.

Account Setup
  1. 1 Go to app.binlookupapi.com/sign-in
  2. 2 Sign in with your Google account
  3. 3 An organisation is created automatically for you

You’ll start on the Development plan which includes 15,000 requests per month with mock responses — perfect for building and testing your integration.

Step 2: Create an API Key

Once logged in:

  1. 1 Navigate to API Keys in the dashboard
  2. 2 Click Create Key
  3. 3 Give it a name (e.g., "C# Development")
  4. 4 Copy the key immediately — it's only shown once

Your API key will look something like: blapi_live_xxxxxxxxxxxxxxxxxxxx

! Security

Store your API key securely. Never commit API keys to version control. Use environment variables or a secrets manager.

Step 3: Your First BIN Lookup

Let’s start with a simple example to verify everything works:

using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

const string ApiKey = "your_api_key_here";
const string ApiUrl = "https://api.binlookupapi.com/v1/bin";

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", ApiKey);

var requestBody = JsonSerializer.Serialize(new { number = 42467101 });
var content = new StringContent(requestBody, Encoding.UTF8, "application/json");

var response = await client.PostAsync(ApiUrl, content);
response.EnsureSuccessStatusCode();

var json = await response.Content.ReadAsStringAsync();
Console.WriteLine(json);

Expected response:

{
  "data": {
    "bin": "42467101",
    "scheme": "visa",
    "funding": "debit",
    "brand": "VISA",
    "category": "CLASSIC",
    "country": {
      "code": "PL",
      "name": "POLAND"
    },
    "issuer": {
      "name": "ING BANK SLASKI SA",
      "website": null,
      "phone": null
    },
    "currency": "PLN",
    "prepaid": false,
    "commercial": false
  }
}

Step 4: Production-Ready Code with Error Handling

The basic example above doesn’t handle errors properly. Here’s a production-ready implementation:

using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace BinLookup;

#region Exceptions

/// <summary>
/// Base exception for all BIN lookup errors.
/// </summary>
public class BinLookupException : Exception
{
    public BinLookupException(string message) : base(message) { }
    public BinLookupException(string message, Exception inner) : base(message, inner) { }
}

/// <summary>
/// Raised when the BIN format is invalid.
/// </summary>
public class InvalidBinException : BinLookupException
{
    public InvalidBinException(string message) : base(message) { }
}

/// <summary>
/// Raised when the API key is invalid, missing, or lacks permissions.
/// </summary>
public class AuthenticationException : BinLookupException
{
    public AuthenticationException(string message) : base(message) { }
}

/// <summary>
/// Raised when the daily quota has been exceeded.
/// </summary>
public class QuotaExceededException : BinLookupException
{
    public QuotaExceededException(string message) : base(message) { }
}

/// <summary>
/// Raised when the BIN is not found in the database.
/// </summary>
public class BinNotFoundException : BinLookupException
{
    public BinNotFoundException(string message) : base(message) { }
}

/// <summary>
/// Raised when the API service encounters an error.
/// </summary>
public class ServiceException : BinLookupException
{
    public ServiceException(string message) : base(message) { }
    public ServiceException(string message, Exception inner) : base(message, inner) { }
}

#endregion

#region Models

/// <summary>
/// Represents issuer (bank) information.
/// </summary>
public sealed record Issuer(
    [property: JsonPropertyName("name")] string? Name,
    [property: JsonPropertyName("website")] string? Website,
    [property: JsonPropertyName("phone")] string? Phone
);

/// <summary>
/// Represents country information.
/// </summary>
public sealed record Country(
    [property: JsonPropertyName("code")] string Code,
    [property: JsonPropertyName("name")] string Name
);

/// <summary>
/// Represents BIN information returned by the API.
/// </summary>
public sealed record BinInfo(
    [property: JsonPropertyName("bin")] string Bin,
    [property: JsonPropertyName("scheme")] string Scheme,
    [property: JsonPropertyName("funding")] string Funding,
    [property: JsonPropertyName("brand")] string? Brand,
    [property: JsonPropertyName("category")] string? Category,
    [property: JsonPropertyName("country")] Country Country,
    [property: JsonPropertyName("issuer")] Issuer Issuer,
    [property: JsonPropertyName("currency")] string? Currency,
    [property: JsonPropertyName("prepaid")] bool Prepaid,
    [property: JsonPropertyName("commercial")] bool Commercial
);

/// <summary>
/// API response wrapper.
/// </summary>
internal sealed record ApiResponse(
    [property: JsonPropertyName("data")] BinInfo Data
);

/// <summary>
/// API error response.
/// </summary>
internal sealed record ApiErrorResponse(
    [property: JsonPropertyName("message")] string? Message
);

/// <summary>
/// Quota information from response headers.
/// </summary>
public sealed record QuotaInfo(
    int Limit,
    int Remaining,
    long ResetTimestamp
);

#endregion

#region Client

/// <summary>
/// Client for the BINLookupAPI service.
/// </summary>
public sealed class BinLookupClient : IDisposable
{
    private const string BaseUrl = "https://api.binlookupapi.com/v1/bin";
    private readonly HttpClient _httpClient;
    private readonly JsonSerializerOptions _jsonOptions;
    private bool _disposed;

    /// <summary>
    /// Initialize the client with an API key.
    /// </summary>
    /// <param name="apiKey">Your BINLookupAPI key. If null, reads from BINLOOKUP_API_KEY environment variable.</param>
    /// <param name="httpClient">Optional HttpClient instance (for IHttpClientFactory usage).</param>
    public BinLookupClient(string? apiKey = null, HttpClient? httpClient = null)
    {
        var key = apiKey ?? Environment.GetEnvironmentVariable("BINLOOKUP_API_KEY");

        if (string.IsNullOrWhiteSpace(key))
        {
            throw new ArgumentException(
                "API key required. Pass it directly or set BINLOOKUP_API_KEY environment variable.",
                nameof(apiKey)
            );
        }

        _httpClient = httpClient ?? new HttpClient();
        _httpClient.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", key);
        _httpClient.Timeout = TimeSpan.FromSeconds(10);

        _jsonOptions = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        };
    }

    /// <summary>
    /// Look up information for a BIN.
    /// </summary>
    /// <param name="binNumber">The BIN to look up (4-8 digits).</param>
    /// <param name="cancellationToken">Cancellation token.</param>
    /// <returns>BinInfo object with card details.</returns>
    /// <exception cref="InvalidBinException">If the BIN format is invalid.</exception>
    /// <exception cref="AuthenticationException">If the API key is invalid.</exception>
    /// <exception cref="QuotaExceededException">If the daily quota is exceeded.</exception>
    /// <exception cref="BinNotFoundException">If the BIN is not found.</exception>
    /// <exception cref="ServiceException">If the API service has an error.</exception>
    public async Task<BinInfo> LookupAsync(int binNumber, CancellationToken cancellationToken = default)
    {
        // Validate input
        if (binNumber < 1000 || binNumber > 99999999)
        {
            throw new InvalidBinException(
                $"BIN must be between 1000 and 99999999, got: {binNumber}"
            );
        }

        HttpResponseMessage response;

        try
        {
            var requestBody = JsonSerializer.Serialize(new { number = binNumber });
            var content = new StringContent(requestBody, Encoding.UTF8, "application/json");

            response = await _httpClient.PostAsync(BaseUrl, content, cancellationToken);
        }
        catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
        {
            throw new ServiceException("Request timed out. Please try again.", ex);
        }
        catch (HttpRequestException ex)
        {
            throw new ServiceException("Could not connect to API. Check your network.", ex);
        }

        // Handle error responses
        var statusCode = (int)response.StatusCode;

        switch (response.StatusCode)
        {
            case HttpStatusCode.BadRequest: // 400
                var errorJson = await response.Content.ReadAsStringAsync(cancellationToken);
                var errorResponse = JsonSerializer.Deserialize<ApiErrorResponse>(errorJson, _jsonOptions);
                throw new InvalidBinException(errorResponse?.Message ?? "Invalid BIN");

            case HttpStatusCode.Unauthorized: // 401
                throw new AuthenticationException("Invalid API key. Check your credentials.");

            case HttpStatusCode.PaymentRequired: // 402
                throw new AuthenticationException("No active subscription. Please subscribe to a plan.");

            case HttpStatusCode.Forbidden: // 403
                throw new AuthenticationException("API key lacks required permissions.");

            case HttpStatusCode.NotFound: // 404
                throw new BinNotFoundException($"BIN {binNumber} not found in database.");

            case (HttpStatusCode)429: // Too Many Requests
                throw new QuotaExceededException("Daily quota exceeded. Resets at midnight UTC.");

            case HttpStatusCode.BadGateway: // 502
                throw new ServiceException("API gateway error. Please try again later.");
        }

        if (statusCode >= 500)
        {
            throw new ServiceException(
                $"API service error (HTTP {statusCode}). Please try again later."
            );
        }

        if (!response.IsSuccessStatusCode)
        {
            throw new BinLookupException($"Unexpected error: HTTP {statusCode}");
        }

        // Parse successful response
        var json = await response.Content.ReadAsStringAsync(cancellationToken);
        var apiResponse = JsonSerializer.Deserialize<ApiResponse>(json, _jsonOptions);

        return apiResponse?.Data ?? throw new BinLookupException("Invalid API response format");
    }

    /// <summary>
    /// Get quota information from the API.
    /// </summary>
    public async Task<QuotaInfo?> GetQuotaInfoAsync(CancellationToken cancellationToken = default)
    {
        try
        {
            var requestBody = JsonSerializer.Serialize(new { number = 42467101 });
            var content = new StringContent(requestBody, Encoding.UTF8, "application/json");

            var response = await _httpClient.PostAsync(BaseUrl, content, cancellationToken);

            if (!response.Headers.TryGetValues("X-Quota-Limit", out var limitValues) ||
                !response.Headers.TryGetValues("X-Quota-Remaining", out var remainingValues) ||
                !response.Headers.TryGetValues("X-Quota-Reset", out var resetValues))
            {
                return null;
            }

            return new QuotaInfo(
                int.Parse(limitValues.First()),
                int.Parse(remainingValues.First()),
                long.Parse(resetValues.First())
            );
        }
        catch
        {
            return null;
        }
    }

    public void Dispose()
    {
        if (!_disposed)
        {
            _httpClient.Dispose();
            _disposed = true;
        }
    }
}

#endregion

Step 5: Using the Client

Here’s how to use the production-ready client:

using BinLookup;

// Initialize with API key from environment
using var client = new BinLookupClient();

// Or pass the key directly
using var clientWithKey = new BinLookupClient(apiKey: "your_api_key_here");

// Look up a BIN
try
{
    var info = await client.LookupAsync(42467101);

    Console.WriteLine($"Card Network: {info.Scheme}");
    Console.WriteLine($"Card Type: {info.Funding}");
    Console.WriteLine($"Issuer: {info.Issuer.Name}");
    Console.WriteLine($"Country: {info.Country.Name}");
    Console.WriteLine($"Is Prepaid: {info.Prepaid}");
}
catch (InvalidBinException ex)
{
    Console.WriteLine($"Invalid BIN: {ex.Message}");
}
catch (AuthenticationException ex)
{
    Console.WriteLine($"Auth error: {ex.Message}");
    // Check your API key
}
catch (QuotaExceededException ex)
{
    Console.WriteLine($"Quota exceeded: {ex.Message}");
    // Wait until midnight UTC or upgrade your plan
}
catch (BinNotFoundException ex)
{
    Console.WriteLine($"BIN not found: {ex.Message}");
    // This BIN isn't in the database
}
catch (ServiceException ex)
{
    Console.WriteLine($"Service error: {ex.Message}");
    // Retry with exponential backoff
}
catch (BinLookupException ex)
{
    Console.WriteLine($"Unexpected error: {ex.Message}");
}
* Error Handling Pattern

The exception hierarchy lets you catch specific errors or use the base BinLookupException to handle all API-related errors at once.

For ASP.NET Core applications, use IHttpClientFactory for proper connection management:

// In Program.cs or Startup.cs
builder.Services.AddHttpClient<BinLookupClient>(client =>
{
    client.BaseAddress = new Uri("https://api.binlookupapi.com/v1/");
    client.Timeout = TimeSpan.FromSeconds(10);
});

builder.Services.AddSingleton(sp =>
{
    var httpClient = sp.GetRequiredService<IHttpClientFactory>()
        .CreateClient(nameof(BinLookupClient));
    var apiKey = builder.Configuration["BinLookupApi:ApiKey"];
    return new BinLookupClient(apiKey, httpClient);
});

Configuration in appsettings.json:

{
  "BinLookupApi": {
    "ApiKey": "your_api_key_here"
  }
}
i Why IHttpClientFactory?

Using IHttpClientFactory prevents socket exhaustion issues and properly manages the lifetime of HttpClient instances in long-running applications.

Step 7: Real-World Example — Payment Form Validation

Here’s a practical example of using BIN lookup in a payment flow:

using BinLookup;

namespace PaymentProcessing;

/// <summary>
/// Result of card BIN validation.
/// </summary>
public sealed record CardValidationResult
{
    public required bool Valid { get; init; }
    public string? CardType { get; init; }
    public string? Issuer { get; init; }
    public string? Country { get; init; }
    public bool IsPrepaid { get; init; }
    public List<string>? Warnings { get; init; }
    public string? Error { get; init; }
}

/// <summary>
/// Service for validating payment card BINs.
/// </summary>
public sealed class CardValidationService
{
    private readonly BinLookupClient _binLookupClient;

    public CardValidationService(BinLookupClient binLookupClient)
    {
        _binLookupClient = binLookupClient;
    }

    /// <summary>
    /// Validate a card BIN for payment processing.
    /// </summary>
    /// <param name="cardNumber">The card number (only first 6-8 digits are used).</param>
    /// <param name="blockPrepaid">If true, reject prepaid cards.</param>
    /// <param name="allowedCountries">List of allowed country codes (e.g., ["US", "GB"]).</param>
    /// <param name="cancellationToken">Cancellation token.</param>
    /// <returns>CardValidationResult with validation details.</returns>
    public async Task<CardValidationResult> ValidateCardBinAsync(
        string cardNumber,
        bool blockPrepaid = false,
        IReadOnlySet<string>? allowedCountries = null,
        CancellationToken cancellationToken = default)
    {
        var warnings = new List<string>();

        // Extract BIN (first 8 digits)
        var digitsOnly = new string(cardNumber.Where(char.IsDigit).ToArray());

        if (digitsOnly.Length < 6)
        {
            return new CardValidationResult
            {
                Valid = false,
                Error = "Card number must be at least 6 digits"
            };
        }

        var binNumber = int.Parse(digitsOnly[..Math.Min(8, digitsOnly.Length)]);

        BinInfo info;

        try
        {
            info = await _binLookupClient.LookupAsync(binNumber, cancellationToken);
        }
        catch (BinNotFoundException)
        {
            return new CardValidationResult
            {
                Valid = false,
                Error = "Unable to identify card. Please check the number."
            };
        }
        catch (QuotaExceededException)
        {
            // Fail open - allow the transaction but log the issue
            return new CardValidationResult
            {
                Valid = true,
                Warnings = ["BIN validation skipped due to quota limits"]
            };
        }
        catch (BinLookupException ex)
        {
            // Fail open for service errors
            return new CardValidationResult
            {
                Valid = true,
                Warnings = [$"BIN validation unavailable: {ex.Message}"]
            };
        }

        // Check prepaid status
        if (blockPrepaid && info.Prepaid)
        {
            return new CardValidationResult
            {
                Valid = false,
                CardType = info.Scheme,
                Issuer = info.Issuer.Name,
                Country = info.Country.Code,
                IsPrepaid = true,
                Error = "Prepaid cards are not accepted"
            };
        }

        // Check country restrictions
        if (allowedCountries is not null && !allowedCountries.Contains(info.Country.Code))
        {
            return new CardValidationResult
            {
                Valid = false,
                CardType = info.Scheme,
                Issuer = info.Issuer.Name,
                Country = info.Country.Code,
                Error = $"Cards from {info.Country.Name} are not accepted"
            };
        }

        // Add warnings for high-risk indicators
        if (info.Prepaid)
        {
            warnings.Add("This is a prepaid card");
        }

        if (info.Commercial)
        {
            warnings.Add("This is a corporate/business card");
        }

        return new CardValidationResult
        {
            Valid = true,
            CardType = info.Scheme,
            Issuer = info.Issuer.Name,
            Country = info.Country.Code,
            IsPrepaid = info.Prepaid,
            Warnings = warnings.Count > 0 ? warnings : null
        };
    }
}

// Usage example
public static class Program
{
    public static async Task Main()
    {
        using var client = new BinLookupClient();
        var validationService = new CardValidationService(client);

        var allowedCountries = new HashSet<string> { "US", "GB", "CA", "AU" };

        var result = await validationService.ValidateCardBinAsync(
            cardNumber: "4246710012345678",
            blockPrepaid: true,
            allowedCountries: allowedCountries
        );

        if (result.Valid)
        {
            Console.WriteLine($"Card accepted: {result.CardType} from {result.Issuer}");

            if (result.Warnings is not null)
            {
                foreach (var warning in result.Warnings)
                {
                    Console.WriteLine($"Warning: {warning}");
                }
            }
        }
        else
        {
            Console.WriteLine($"Card rejected: {result.Error}");
        }
    }
}
i Fail Open Strategy

For payment validation, consider “failing open” when the API is unavailable. It’s usually better to allow a transaction and verify later than to block all payments during an outage.

Best Practices Summary

Production Checklist
  • Store API key in environment variable or IConfiguration
  • Use IHttpClientFactory in ASP.NET Core applications
  • Handle all error types: network, auth, quota, not-found
  • Set request timeouts (10 seconds recommended)
  • Implement retry logic with exponential backoff for transient errors
  • Consider fail-open strategy for non-critical validation
  • Monitor your quota usage via response headers
  • Use CancellationToken for proper async cancellation support

Next Steps

Ready to get started? Create your free account and start building today.