BIN Lookup API with cURL: Integration Guide
This guide walks you through integrating BINLookupAPI using cURL and Bash scripts, from creating your account to writing production-ready shell scripts with proper error handling.
- ✓ Look up BIN information using cURL commands
- ✓ Create reusable shell script wrapper functions
- ✓ Parse JSON responses with jq
- ✓ Handle all error cases gracefully
- ✓ Process multiple BINs in batch operations
- ✓ Validate cards in a real payment flow script
Prerequisites
- Bash 4.0 or higher (or zsh)
- cURL (installed by default on most Unix systems)
- jq (for JSON parsing)
Install jq if you don’t have it:
# macOS
brew install jq
# Ubuntu/Debian
sudo apt-get install jq
# CentOS/RHEL
sudo yum install jq
# Alpine
apk add jq
Step 1: Create Your Account
First, you’ll need a BINLookupAPI account to get your API key.
- 1 Go to app.binlookupapi.com/sign-in
- 2 Sign in with your Google account
- 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 Navigate to API Keys in the dashboard
- 2 Click Create Key
- 3 Give it a name (e.g., "CLI Development")
- 4 Copy the key immediately — it's only shown once
Your API key will look something like: blapi_live_xxxxxxxxxxxxxxxxxxxx
Store your API key in an environment variable. Never hardcode API keys in scripts or commit them to version control.
Set up your API key as an environment variable:
# Add to your ~/.bashrc, ~/.zshrc, or ~/.profile
export BINLOOKUP_API_KEY="blapi_live_xxxxxxxxxxxxxxxxxxxx"
# Reload your shell configuration
source ~/.bashrc # or ~/.zshrc
Step 3: Your First BIN Lookup
Let’s start with a simple cURL command to verify everything works:
curl -X POST https://api.binlookupapi.com/v1/bin \
-H "Authorization: Bearer $BINLOOKUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number": 42467101}'
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
}
}
To pretty-print the response, pipe it through jq:
curl -s -X POST https://api.binlookupapi.com/v1/bin \
-H "Authorization: Bearer $BINLOOKUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number": 42467101}' | jq .
The -s flag silences cURL’s progress output, making it easier to pipe the response to jq or other tools.
Step 4: Shell Script Wrapper Function
The basic cURL command above doesn’t handle errors properly. Here’s a production-ready shell function:
#!/usr/bin/env bash
# BIN Lookup API wrapper function
# Requires: BINLOOKUP_API_KEY environment variable
BINLOOKUP_API_URL="https://api.binlookupapi.com/v1/bin"
lookup_bin() {
local bin_number="$1"
local response
local http_code
local body
# Validate input
if [[ -z "$bin_number" ]]; then
echo "Error: BIN number required" >&2
return 1
fi
if [[ ! "$bin_number" =~ ^[0-9]{4,8}$ ]]; then
echo "Error: BIN must be 4-8 digits" >&2
return 1
fi
# Check for API key
if [[ -z "$BINLOOKUP_API_KEY" ]]; then
echo "Error: BINLOOKUP_API_KEY environment variable not set" >&2
return 1
fi
# Make the API request and capture both body and HTTP status code
response=$(curl -s -w "\n%{http_code}" -X POST "$BINLOOKUP_API_URL" \
-H "Authorization: Bearer $BINLOOKUP_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"number\": $bin_number}" \
--connect-timeout 10 \
--max-time 30)
# Extract HTTP status code (last line) and body (everything else)
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
# Handle HTTP status codes
case "$http_code" in
200)
echo "$body"
return 0
;;
400)
echo "Error: Invalid BIN format - $(echo "$body" | jq -r '.message // "Bad request"')" >&2
return 1
;;
401)
echo "Error: Invalid API key. Check your BINLOOKUP_API_KEY." >&2
return 1
;;
402)
echo "Error: No active subscription. Please subscribe to a plan." >&2
return 1
;;
403)
echo "Error: API key lacks required permissions." >&2
return 1
;;
404)
echo "Error: BIN $bin_number not found in database." >&2
return 1
;;
429)
echo "Error: Daily quota exceeded. Resets at midnight UTC." >&2
return 1
;;
502|503|504)
echo "Error: API service temporarily unavailable (HTTP $http_code). Try again later." >&2
return 1
;;
*)
echo "Error: Unexpected HTTP status $http_code" >&2
echo "$body" >&2
return 1
;;
esac
}
# Example usage (uncomment to test):
# lookup_bin 42467101 | jq .
Save this to a file like bin_lookup.sh and source it:
source bin_lookup.sh
# Now you can use the function
lookup_bin 42467101 | jq .
Step 5: Parsing Responses with jq
jq is essential for extracting specific fields from the API response. Here are common patterns:
# Get the full data object
lookup_bin 42467101 | jq '.data'
# Get specific fields
lookup_bin 42467101 | jq '.data.scheme' # "visa"
lookup_bin 42467101 | jq '.data.funding' # "debit"
lookup_bin 42467101 | jq '.data.country.name' # "POLAND"
lookup_bin 42467101 | jq '.data.issuer.name' # "ING BANK SLASKI SA"
lookup_bin 42467101 | jq '.data.prepaid' # false
# Get multiple fields as a new object
lookup_bin 42467101 | jq '{
bin: .data.bin,
network: .data.scheme,
type: .data.funding,
country: .data.country.name,
issuer: .data.issuer.name,
prepaid: .data.prepaid
}'
# Get raw values without quotes (useful for shell variables)
scheme=$(lookup_bin 42467101 | jq -r '.data.scheme')
echo "Card network: $scheme"
# Check boolean fields
is_prepaid=$(lookup_bin 42467101 | jq -r '.data.prepaid')
if [[ "$is_prepaid" == "true" ]]; then
echo "This is a prepaid card"
fi
Use jq -r (raw output) when you need to use the value in shell scripts. It removes the surrounding quotes from strings.
Step 6: Batch Processing Multiple BINs
For processing multiple BINs, here’s an efficient batch script:
#!/usr/bin/env bash
# Batch BIN lookup script
# Usage: ./batch_lookup.sh bins.txt
source bin_lookup.sh # Include the lookup_bin function
process_bins() {
local input_file="$1"
local output_format="${2:-json}" # json or csv
local success_count=0
local error_count=0
if [[ ! -f "$input_file" ]]; then
echo "Error: Input file '$input_file' not found" >&2
return 1
fi
# Print CSV header if needed
if [[ "$output_format" == "csv" ]]; then
echo "bin,scheme,funding,country,issuer,prepaid,commercial"
fi
while IFS= read -r bin || [[ -n "$bin" ]]; do
# Skip empty lines and comments
[[ -z "$bin" || "$bin" =~ ^# ]] && continue
# Clean the BIN (remove spaces, dashes)
bin=$(echo "$bin" | tr -d ' -')
# Rate limiting: 100ms delay between requests
sleep 0.1
response=$(lookup_bin "$bin" 2>/dev/null)
if [[ $? -eq 0 ]]; then
((success_count++))
if [[ "$output_format" == "csv" ]]; then
# Output as CSV
echo "$response" | jq -r '[
.data.bin,
.data.scheme,
.data.funding,
.data.country.code,
.data.issuer.name // "N/A",
.data.prepaid,
.data.commercial
] | @csv'
else
# Output as JSON (one object per line)
echo "$response" | jq -c '.data'
fi
else
((error_count++))
echo "# Error looking up BIN: $bin" >&2
fi
done < "$input_file"
echo "# Processed: $success_count successful, $error_count errors" >&2
}
# Run if executed directly (not sourced)
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
if [[ $# -lt 1 ]]; then
echo "Usage: $0 <input_file> [json|csv]" >&2
exit 1
fi
process_bins "$1" "${2:-json}"
fi
Create a file with BINs to process:
# bins.txt - one BIN per line
42467101
51234567
37123456
# This is a comment
60110012
Run the batch processor:
# Output as JSON lines
./batch_lookup.sh bins.txt > results.jsonl
# Output as CSV
./batch_lookup.sh bins.txt csv > results.csv
The script includes a 100ms delay between requests to avoid hitting rate limits. Adjust this based on your plan’s rate limits.
Step 7: Real-World Example — Payment Validation Script
Here’s a practical script for validating cards in a payment processing workflow:
#!/usr/bin/env bash
# Payment Card Validation Script
# Validates a card BIN against business rules
set -euo pipefail
BINLOOKUP_API_URL="https://api.binlookupapi.com/v1/bin"
# Configuration (customize these)
BLOCK_PREPAID="${BLOCK_PREPAID:-false}"
ALLOWED_COUNTRIES="${ALLOWED_COUNTRIES:-}" # e.g., "US,GB,CA,AU"
BLOCKED_COUNTRIES="${BLOCKED_COUNTRIES:-}" # e.g., "RU,IR,KP"
validate_card() {
local card_number="$1"
local result
local http_code
local body
# Extract first 8 digits (BIN)
local bin=$(echo "$card_number" | tr -dc '0-9' | cut -c1-8)
if [[ ${#bin} -lt 6 ]]; then
echo '{"valid": false, "error": "Card number must be at least 6 digits"}'
return 1
fi
# Make API request
result=$(curl -s -w "\n%{http_code}" -X POST "$BINLOOKUP_API_URL" \
-H "Authorization: Bearer $BINLOOKUP_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"number\": $bin}" \
--connect-timeout 10 \
--max-time 30 2>/dev/null) || true
http_code=$(echo "$result" | tail -n1)
body=$(echo "$result" | sed '$d')
# Handle errors
case "$http_code" in
200)
# Success - continue with validation
;;
404)
echo '{"valid": false, "error": "Unable to identify card. Please check the number."}'
return 1
;;
429)
# Fail open on quota exceeded
echo '{"valid": true, "warning": "BIN validation skipped due to quota limits"}'
return 0
;;
*)
# Fail open on service errors
echo "{\"valid\": true, \"warning\": \"BIN validation unavailable (HTTP $http_code)\"}"
return 0
;;
esac
# Parse response
local scheme=$(echo "$body" | jq -r '.data.scheme')
local funding=$(echo "$body" | jq -r '.data.funding')
local country_code=$(echo "$body" | jq -r '.data.country.code')
local country_name=$(echo "$body" | jq -r '.data.country.name')
local issuer=$(echo "$body" | jq -r '.data.issuer.name // "Unknown"')
local is_prepaid=$(echo "$body" | jq -r '.data.prepaid')
local is_commercial=$(echo "$body" | jq -r '.data.commercial')
local warnings=()
# Check prepaid restriction
if [[ "$BLOCK_PREPAID" == "true" && "$is_prepaid" == "true" ]]; then
jq -n \
--arg scheme "$scheme" \
--arg issuer "$issuer" \
--arg country "$country_code" \
'{
valid: false,
card_type: $scheme,
issuer: $issuer,
country: $country,
is_prepaid: true,
error: "Prepaid cards are not accepted"
}'
return 1
fi
# Check country restrictions
if [[ -n "$ALLOWED_COUNTRIES" ]]; then
if [[ ! ",$ALLOWED_COUNTRIES," =~ ",$country_code," ]]; then
jq -n \
--arg scheme "$scheme" \
--arg issuer "$issuer" \
--arg country "$country_code" \
--arg country_name "$country_name" \
'{
valid: false,
card_type: $scheme,
issuer: $issuer,
country: $country,
error: ("Cards from " + $country_name + " are not accepted")
}'
return 1
fi
fi
if [[ -n "$BLOCKED_COUNTRIES" ]]; then
if [[ ",$BLOCKED_COUNTRIES," =~ ",$country_code," ]]; then
jq -n \
--arg scheme "$scheme" \
--arg issuer "$issuer" \
--arg country "$country_code" \
--arg country_name "$country_name" \
'{
valid: false,
card_type: $scheme,
issuer: $issuer,
country: $country,
error: ("Cards from " + $country_name + " are not accepted")
}'
return 1
fi
fi
# Build warnings array
local warnings_json="[]"
if [[ "$is_prepaid" == "true" ]]; then
warnings_json=$(echo "$warnings_json" | jq '. + ["This is a prepaid card"]')
fi
if [[ "$is_commercial" == "true" ]]; then
warnings_json=$(echo "$warnings_json" | jq '. + ["This is a corporate/business card"]')
fi
# Return success result
jq -n \
--arg scheme "$scheme" \
--arg funding "$funding" \
--arg issuer "$issuer" \
--arg country "$country_code" \
--argjson prepaid "$is_prepaid" \
--argjson commercial "$is_commercial" \
--argjson warnings "$warnings_json" \
'{
valid: true,
card_type: $scheme,
funding: $funding,
issuer: $issuer,
country: $country,
is_prepaid: $prepaid,
is_commercial: $commercial,
warnings: (if ($warnings | length) > 0 then $warnings else null end)
}'
return 0
}
# Main execution
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
if [[ $# -lt 1 ]]; then
echo "Usage: $0 <card_number>" >&2
echo "" >&2
echo "Environment variables:" >&2
echo " BINLOOKUP_API_KEY - Required. Your API key" >&2
echo " BLOCK_PREPAID - Block prepaid cards (true/false)" >&2
echo " ALLOWED_COUNTRIES - Comma-separated country codes to allow" >&2
echo " BLOCKED_COUNTRIES - Comma-separated country codes to block" >&2
exit 1
fi
validate_card "$1"
fi
Example usage:
# Basic validation
./validate_card.sh 4246710012345678
# Block prepaid cards
BLOCK_PREPAID=true ./validate_card.sh 4246710012345678
# Only allow specific countries
ALLOWED_COUNTRIES="US,GB,CA,AU" ./validate_card.sh 4246710012345678
# Block specific countries
BLOCKED_COUNTRIES="RU,IR,KP" ./validate_card.sh 4246710012345678
For payment validation, the script “fails 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.
Alternative: Using HTTPie
If you prefer a more user-friendly HTTP client, HTTPie is an excellent alternative to cURL:
# Install HTTPie
pip install httpie
# Basic request
http POST https://api.binlookupapi.com/v1/bin \
Authorization:"Bearer $BINLOOKUP_API_KEY" \
number:=42467101
# HTTPie automatically formats JSON output
# Use --print=b to print only the body
http --print=b POST https://api.binlookupapi.com/v1/bin \
Authorization:"Bearer $BINLOOKUP_API_KEY" \
number:=42467101
# Check response headers (quota info)
http --print=h POST https://api.binlookupapi.com/v1/bin \
Authorization:"Bearer $BINLOOKUP_API_KEY" \
number:=42467101
Note the := syntax for numbers in HTTPie. This sends the value as a JSON number rather than a string.
Checking API Quota
Monitor your API quota using the response headers:
# Get quota information from response headers
curl -s -D - -X POST https://api.binlookupapi.com/v1/bin \
-H "Authorization: Bearer $BINLOOKUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number": 42467101}' \
-o /dev/null | grep -i "x-quota"
# Output:
# X-Quota-Limit: 15000
# X-Quota-Remaining: 14999
# X-Quota-Reset: 1706918400
A helper function to check quota:
check_quota() {
local headers
headers=$(curl -s -D - -X POST "$BINLOOKUP_API_URL" \
-H "Authorization: Bearer $BINLOOKUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number": 42467101}' \
-o /dev/null)
local limit=$(echo "$headers" | grep -i "x-quota-limit" | cut -d' ' -f2 | tr -d '\r')
local remaining=$(echo "$headers" | grep -i "x-quota-remaining" | cut -d' ' -f2 | tr -d '\r')
local reset=$(echo "$headers" | grep -i "x-quota-reset" | cut -d' ' -f2 | tr -d '\r')
local reset_date=$(date -r "$reset" 2>/dev/null || date -d "@$reset" 2>/dev/null || echo "N/A")
echo "Quota Limit: $limit"
echo "Quota Remaining: $remaining"
echo "Quota Resets: $reset_date"
}
Best Practices Summary
- ✓ Store API key in BINLOOKUP_API_KEY environment variable
- ✓ Use the -s (silent) flag to suppress cURL progress output
- ✓ Always capture and check HTTP status codes
- ✓ Handle all error types: 400, 401, 402, 403, 404, 429, 5xx
- ✓ Set connection and request timeouts (--connect-timeout, --max-time)
- ✓ Use jq for reliable JSON parsing (never parse with grep/sed)
- ✓ Implement rate limiting in batch scripts (sleep between requests)
- ✓ Consider fail-open strategy for non-critical validation
- ✓ Monitor your quota usage via X-Quota-* response headers
- ✓ Quote variables properly to handle special characters
Next Steps
- View the full API Reference for all available fields and error codes
- Explore pricing plans to find the right quota for your needs
- Contact support if you need help with your integration
Ready to get started? Create your free account and start building today.