Routing Number 021000021 API in PHP

Routing Number 021000021 API in PHP

You need to verify that US routing number 021000021 is valid before you release or accept an ACH or wire transfer. By the end of this guide you will: validate that routing number via a POST request, integrate the check in PHP, handle “not found” and error cases, and decide what to cache so your payment flow is resilient and fast.

What a US routing number is and how validation works

A US routing transit number (ABA RTN) is a 9-digit identifier used to route domestic ACH and wire transfers between financial institutions in the United States. It encodes a Federal Reserve district prefix, an institution identifier, and a check digit. A basic validation performs two checks:

  • Format: exactly 9 numeric digits, no spaces or punctuation.
  • Checksum: the ABA modulus-10 weighted calculation on the first 8 digits must produce the 9th check digit.

The checksum is a quick integrity check but does not tell you which bank or branch is behind the number. A lookup service like BankDataStack returns the corresponding bank name, branch, city, and country so you can confirm that the provided details match the customer or counterparty before you move funds.

Endpoint overview for routing number validation

Use the routing validation endpoint to verify 021000021:

  • Method: POST
  • URL: https://www.bankdatastack.com/api/v1/routing/validate
  • Authentication: X-API-Key header
  • Body: JSON payload containing the routing number

The service responds with JSON including whether the number is valid and, if known, the issuing bank and location. This is ideal in onboarding (pre-funding) and pre-transfer validation to catch typos and detect mismatches early. Review the full parameter and response structure in the Documentation.

cURL request you can copy

Here’s a complete request that validates the specific routing number 021000021. Replace YOUR_API_KEY with your key.

curl -sS https://www.bankdatastack.com/api/v1/routing/validate \
-X POST \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"routing":"021000021"}'

PHP integration for routing 021000021

The following PHP example uses ext-curl, which is available in most PHP runtimes. It sends the same POST request, checks HTTP status, parses JSON, and then acts on the fields your ACH/wire flow needs.

<?php
$routing = "021000021";
$apiUrl = "https://www.bankdatastack.com/api/v1/routing/validate";
$apiKey = "YOUR_API_KEY";

$payload = json_encode(["routing" => $routing], JSON_UNESCAPED_SLASHES);

$ch = curl_init($apiUrl);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: " . $apiKey
],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 10, // seconds
CURLOPT_CONNECTTIMEOUT => 5, // seconds
]);

$responseBody = curl_exec($ch);
$curlErr = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($responseBody === false) {
// Transport error: surface a retriable error to the caller
http_response_code(503);
echo json_encode(["error" => "Upstream validation error: " . $curlErr]);
exit;
}

if ($httpCode < 200 || $httpCode >= 300) {
// Non-2xx from API; log and return a safe error
error_log("BankDataStack non-2xx ($httpCode): $responseBody");
http_response_code(502);
echo json_encode(["error" => "Validation service unavailable"]);
exit;
}

$data = json_decode($responseBody, true);
if (!is_array($data)) {
http_response_code(502);
echo json_encode(["error" => "Invalid response format"]);
exit;
}

// Expected fields: valid (bool), routing (string), bank (string), branch (string), city (string), country (string)
// Use defaults in case fields are missing
$valid = $data["valid"] ?? false;
$bank = $data["bank"] ?? null;
$branch = $data["branch"] ?? null;
$city = $data["city"] ?? null;
$country= $data["country"]?? null;

if (!$valid) {
// Reject before initiating an ACH/wire
echo json_encode([
"status" => "rejected",
"reason" => "invalid_or_not_found_routing",
"routing" => $routing
]);
exit;
}

// Optionally compare user-declared bank name/city with the reference data
// e.g., if the user typed "Chase" and city "New York", assert a fuzzy match

echo json_encode([
"status" => "accepted",
"routing" => $routing,
"reference" => [
"bank" => $bank,
"branch" => $branch,
"city" => $city,
"country" => $country
]
]);

Plug this into your account or payout onboarding form. When a user enters an account number and routing number, validate the routing number first. If it is valid and the bank metadata matches expectations, you can proceed to store the masked account number for later micro-deposit verification or instant account checks.

JavaScript example (Node.js)

If you prefer a quick server-side check in Node.js (for back-office ops tools or serverless functions), this example calls the same endpoint and inspects the same fields.

import fetch from "node-fetch";

const API_URL = "https://www.bankdatastack.com/api/v1/routing/validate";
const API_KEY = "YOUR_API_KEY";

async function validateRouting(routing) {
const res = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": API_KEY
},
body: JSON.stringify({ routing })
});

if (!res.ok) {
const text = await res.text();
throw new Error(`Validation service error ${res.status}: ${text}`);
}

const data = await res.json();
// Expected fields: valid, routing, bank, branch, city, country
return data;
}

validateRouting("021000021")
.then(result => {
if (result.valid) {
console.log("Routing is valid:", {
bank: result.bank,
branch: result.branch,
city: result.city,
country: result.country
});
} else {
console.log("Invalid or not found routing:", result.routing);
}
})
.catch(err => {
console.error("Lookup failed:", err.message);
});

Sample JSON response

The following is an illustrative response showing the core fields you will typically use. Field names are representative of what BankDataStack returns for routing lookups.

{
"valid": true,
"routing": "021000021",
"bank": "Example Bank Name",
"branch": "Main Office",
"city": "New York",
"country": "US"
}

How to use these fields in your flow:

  • valid: Gate your ACH or wire initiation logic. Only proceed when true.
  • routing: Echo back or store alongside the payment instruction for audit trails.
  • bank/branch/city/country: Cross-check against user-declared information. For example, if a payer expects a New York-based institution but the data says a different city, prompt the user to re-verify.

Handling not found and error scenarios

Interpreting responses is critical to avoid false positives:

  • HTTP 2xx with valid=false: The routing number failed format or checksum checks, or it does not exist in the reference dataset. Do not send funds; ask the user to correct it.
  • HTTP 2xx with valid=true but missing some metadata: The number is structurally valid, but non-critical fields (e.g., branch) may be unavailable. You can proceed, optionally adding extra verification.
  • HTTP 4xx: Your request is malformed (e.g., missing routing), or your API key is invalid. Fix the request before retrying.
  • HTTP 5xx or network timeout: Treat as a transient upstream failure. Retry with exponential backoff or surface a “try again” message to the user without losing form state.

Where this fits in your payments and onboarding flows

For ACH credit/debit and domestic wires in the US, run the routing validation at two checkpoints:

  • During account capture: When the user types the routing number, validate and immediately show bank name and city. This reduces support load due to typos.
  • Before submission: Re-validate server-side just prior to queuing the transfer, ensuring the value hasn’t changed and your cache is fresh.

Combine routing validation with account-number verification steps such as micro-deposits or the tokenization method you use. While BankDataStack returns the issuing institution details, it does not authenticate ownership of the destination account. Treat this as a reference and validation step, not KYC/AML or account ownership proof.

Checksum basics for US routing numbers

Routing numbers use a modulus-10 checksum based on weighted sums of the first eight digits. A high-level description is:

  • Multiply digits 1, 4, and 7 by 3; digits 2, 5, and 8 by 7; and digits 3 and 6 by 1.
  • Sum those products.
  • The check digit (9th) makes the total a multiple of 10.

This prevents common transposition and single-digit errors at the point of data entry. You still need a directory lookup to identify the bank and city and to confirm active usage for ACH or wire flows.

Caching and data freshness

Routing numbers do not change frequently, but they can be reassigned or retired. To balance performance and freshness:

  • Cache positive validations (valid=true) for 24 hours keyed by routing number. This removes redundant round trips in forms where users may re-submit the same details.
  • Cache negative validations (valid=false) briefly, e.g., 15 minutes, to avoid locking out a user who corrects a typo.
  • Include the resolved bank and city in your cache entry and show that to the user immediately on subsequent loads.
  • Invalidate the cache when you change environments (sandbox to production) or rotate API keys, to avoid mixing datasets by accident.

Comparing routing numbers with other identifiers

If you work across regions or process multiple payment rails, it helps to keep formats straight. Here’s a quick comparison of commonly validated identifiers you may touch in the same integration.

Identifier Region/Rail Format Checksum Primary Use
Routing (ABA) USA 9 digits Yes (ABA modulus-10) ACH and domestic wires
IBAN EMEA/Global (not USA) Country-specific length; starts with 2 letters Yes (mod-97) Cross-border and domestic in IBAN countries
SWIFT/BIC Global 8 or 11 alphanumeric (bank, country, location, branch) Structural checks only Bank identification for cross-border wires
BIN (Card) Global First 6+ digits of a PAN Part of Luhn on full PAN Issuer identification and routing for card payments

Note: The USA does not use IBAN. Use IBAN validation for countries where it is mandated; for example, GB82WEST12345698765432 is a valid IBAN in the United Kingdom and can be validated with the IBAN endpoint. For cross-border wires, pair beneficiary IBAN (when applicable) with a SWIFT/BIC such as CHASUS33.

Security and data handling notes

  • Never log or persist full primary account numbers (PANs). When dealing with cards, the BIN is only the first digits used for issuer lookup; do not store full card data.
  • Restrict your BankDataStack API key to server-side use. Do not expose it in client-side JavaScript shipped to browsers.
  • Set reasonable timeouts (e.g., 5s connect, 10s total) and retry with backoff on transient network errors.
  • Log the request correlation IDs or timestamps around validation calls to aid audits and support investigations.

Testing with known fixtures

For local or CI testing, keep a suite of fixtures handy to avoid brittle tests that depend on live banking changes. BankDataStack provides well-known identifiers you can use to verify your request/response handling:

  • Routing: 021000021 (this article’s focus)
  • IBAN: GB82WEST12345698765432
  • BIN: 424242
  • SWIFT/BIC: CHASUS33

Use the routing number above for your PHP integration tests to cover the positive path (valid=true). Include negative tests with made-up 9-digit numbers that fail checksum, and confirm your code blocks submission and shows actionable messages.

Putting it all together in your flow

Onboarding form

  • User enters routing number and account number.
  • Your frontend sends the routing number to your backend.
  • Backend calls BankDataStack’s routing validation. If valid, return bank and city to the frontend for display. If invalid, ask the user to correct it.

Pre-transfer verification

  • Server re-validates routing number from stored instructions.
  • Server compares cached bank metadata; if a mismatch appears (e.g., updated bank assignment), require a user confirmation before releasing funds.

Pricing and getting access

BankDataStack’s Starter plan is $49.99/month with a 7-day trial, suitable for launching routing validation in production. To start, create your account and generate an API key. You can implement everything shown above within an afternoon.

Register for your API key, then head over to the Documentation for more endpoint details.

FAQ

Does a valid routing number guarantee that an ACH or wire will settle?
No. Validation confirms format, checksum, and directory lookup (bank and location). Settlement still depends on a valid destination account, sufficient funds, risk controls, and network status.

Should I store the routing validation result?
Yes. Cache the result along with bank and city for at least 24 hours. Keep a timestamp so you can decide when to refresh before initiating a transfer.

What does “not found” mean in practice?
Either the number fails checksum/format or it is absent from the reference dataset. Treat it as invalid and request a correction from the user. Do not attempt to guess a nearby number.

Can I validate IBANs or SWIFT/BICs with the same pattern?
Yes. Use POST https://www.bankdatastack.com/api/v1/iban/validate for IBANs (e.g., GB82WEST12345698765432) and POST https://www.bankdatastack.com/api/v1/swift/validate for SWIFT/BICs (e.g., CHASUS33). The USA does not use IBAN, but many other countries do.

Do I need to store full card numbers to use BIN lookups?
No. Never store full PANs. BIN validation uses only the first digits of the card number to identify the issuer and card type.

Ready to validate routing number 021000021 from PHP and ship a safer ACH/wire flow? Create your API key now with the 7-day trial and implement the endpoint above: Register. For more details on request and response payloads, see the Documentation.

Ready to get started?

Get your API key and start validating bank data in minutes.

Get API Key

Related posts