Skip to content

Latest commit

 

History

History
311 lines (246 loc) · 8.48 KB

File metadata and controls

311 lines (246 loc) · 8.48 KB

API Documentation

Complete API reference for the FFL Verify service.

API Overview

graph LR
    Client[Client]

    subgraph "API Endpoints /api/v1"
        Health["/health<br/>Service Status"]
        Stats["/stats<br/>Database Stats"]
        Lookup["/ffl/:number<br/>Lookup by Number"]
        Search["/ffl/search<br/>Search & Filter"]
    end

    Client -->|GET| Health
    Client -->|GET| Stats
    Client -->|GET| Lookup
    Client -->|GET| Search
Loading

Base URL

http://localhost:8080/api/v1

Authentication

No authentication required. API is rate-limited to 100 requests per minute per IP address.

Endpoints

Health Check

Check service status and record count.

Endpoint: GET /health

Response (200 OK):

{
  "status": "ok",
  "records_loaded": 45892
}

Statistics

Get database statistics and last updated timestamp.

Endpoint: GET /stats

Response (200 OK):

{
  "total_records": 45892,
  "last_updated": "2025-11-08T19:40:13Z"
}

FFL Lookup

Look up FFL license information by license number.

Endpoint: GET /ffl/{ffl_number}

Parameters:

  • ffl_number (path) - FFL number in any of the following formats:
    • Short with dashes: X-XX-XXXXX (e.g., 5-84-12875)
    • Short without dashes: XXXXXXXXX or XXXXXXXX (e.g., 584012875 or 58412875)
    • Full with dashes: X-XX-XXX-XX-XX-XXXXX (e.g., 5-84-059-01-8C-12875)
    • Full without dashes: XXXXXXXXXXXXXXX or XXXXXXXXXXXXXX (e.g., 584059018C12875)

Success Response (200 OK):

{
  "ffl_number": "1-66-00332",
  "ffl_number_full": "1-66-003-01-6L-00332",
  "active": true,
  "exp": "2026-12-01",
  "exp_formatted": "December 1, 2026",
  "ezcheck_url": "https://www.ffls.com/ffl/redirecttoezcheck/166003016l00332",
  "license_name": "AGUADA ARMORY SHOOTING AND RENTAL CORP",
  "business_name": "",
  "premise_street": "4 CALLE COLON SUITE 3",
  "premise_city": "AGUADA",
  "premise_state": "PR",
  "premise_zip_code": "00602",
  "mail_street": "PO BOX 1234",
  "mail_city": "AGUADA",
  "mail_state": "PR",
  "mail_zip_code": "00603",
  "voice_phone": "(787) 555-1234",
  "definition": {
    "irs_region": "1",
    "district": "66",
    "fips_code": "003",
    "type": "01",
    "type_description": "Dealer in Firearms Other Than Destructive Devices (Includes Gunsmiths)",
    "expires": "December 1, 2026",
    "sequence_number": "00332"
  }
}

Response Fields:

  • ffl_number - Display format FFL number (X-XX-XXXXX)
  • ffl_number_full - Full 15-digit FFL number (X-XX-XXX-XX-XX-XXXXX)
  • active - Whether the license is currently active (not expired)
  • exp - Expiration date in ISO 8601 format (YYYY-MM-DD)
  • exp_formatted - Human-readable expiration date
  • ezcheck_url - Direct link to verify license on eZ Check
  • license_name - Name on the FFL license
  • business_name - Business name (if different from license name)
  • premise_* - Licensed premise address fields
  • mail_* - Mailing address fields (if different from premise)
  • voice_phone - Contact phone number
  • definition - Detailed breakdown of FFL components

Error Responses:

  • 400 Bad Request - Invalid FFL number format
  • 404 Not Found - FFL number not found
  • 429 Too Many Requests - Rate limit exceeded

Examples:

# Abbreviated format (with dashes)
curl http://localhost:8080/api/v1/ffl/1-66-00332

# Abbreviated format (without dashes)
curl http://localhost:8080/api/v1/ffl/16600332

# Full format (with dashes)
curl http://localhost:8080/api/v1/ffl/1-66-003-01-6L-00332

# Full format (without dashes)
curl http://localhost:8080/api/v1/ffl/166003016L00332

# Pretty print
curl -s http://localhost:8080/api/v1/ffl/1-66-00332 | jq .

FFL Search

Search for FFL licenses by location, name, or other criteria.

Endpoint: GET /ffl/search

Query Parameters:

Location-Based Search (requires server started with -geocode-ffls=true):

  • zip - 5-digit US ZIP code (server geocodes to lat/lon)
  • lat - Latitude (-90 to 90)
  • lon - Longitude (-180 to 180) - required if lat is provided
  • radius - Search radius in miles (1-500, default varies by search type)

Text/Filter Search (always available):

  • name - Search by license name or business name (case-insensitive partial match)
  • city - Filter by city name (case-insensitive)
  • state - Filter by state (2-letter code, e.g., AZ, CA)
  • type - Filter by license type code (e.g., 01, 07)

General Parameters:

  • active - Filter to active licenses only (true/false)
  • limit - Maximum results to return (1-100, default 50)
  • offset - Result offset for pagination (default 0)
  • sort - Sort field (currently supports "distance" for proximity searches)

Requirements:

  • At least one search criterion required (zip, lat/lon, name, city, state, or type)
  • lat and lon must be provided together
  • sort=distance requires location-based search (zip or lat/lon)
  • Proximity search requires FFL geocoding - Server must be started with -geocode-ffls=true flag

Success Response (200 OK):

{
  "results": [
    {
      "ffl_number": "1-66-00332",
      "ffl_number_full": "1-66-003-01-6L-00332",
      "active": true,
      "exp": "2026-12-01",
      "exp_formatted": "December 1, 2026",
      "license_name": "AGUADA ARMORY SHOOTING",
      "business_name": "",
      "premise_street": "4 CALLE COLON SUITE 3",
      "premise_city": "AGUADA",
      "premise_state": "PR",
      "premise_zip_code": "00602",
      "latitude": 18.3937,
      "longitude": -67.1897,
      "geocoded": true,
      "distance": 12.5,
      "definition": {
        "irs_region": "1",
        "district": "66",
        "fips_code": "003",
        "type": "01",
        "type_description": "Dealer in Firearms Other Than Destructive Devices",
        "expires": "December 1, 2026",
        "sequence_number": "00332"
      }
    }
  ],
  "total": 45,
  "limit": 50,
  "offset": 0
}

Response Fields:

  • results - Array of FFL records matching search criteria
  • total - Total number of matches (before pagination)
  • limit - Applied result limit
  • offset - Applied result offset
  • distance - Distance in miles (only present for proximity searches)
  • geocoded - Whether coordinates are available for this FFL

Error Responses:

  • 400 Bad Request - Invalid parameters, missing required criteria, or proximity search attempted without FFL geocoding enabled
  • 429 Too Many Requests - Rate limit exceeded
  • 500 Internal Server Error - Server error

Common Error Messages:

{
  "error": "proximity search requires FFL geocoding - restart server with -geocode-ffls=true flag"
}

This error occurs when attempting ZIP/lat/lon search without FFL geocoding enabled.

Examples:

# Proximity search by ZIP code
curl "http://localhost:8080/api/v1/ffl/search?zip=85001&radius=25"

# Proximity search by coordinates
curl "http://localhost:8080/api/v1/ffl/search?lat=33.4484&lon=-112.0740&radius=50"

# Search by business name
curl "http://localhost:8080/api/v1/ffl/search?name=gun+shop"

# Search by city and state
curl "http://localhost:8080/api/v1/ffl/search?city=Phoenix&state=AZ"

# Filter by license type
curl "http://localhost:8080/api/v1/ffl/search?state=CA&type=01&active=true"

# Combined search with pagination
curl "http://localhost:8080/api/v1/ffl/search?zip=90028&radius=100&type=01&limit=25&offset=0"

# Pretty print results
curl -s "http://localhost:8080/api/v1/ffl/search?zip=85001&radius=25" | jq .

License Types

Common FFL license type codes:

Code Description
01 Dealer in Firearms Other Than Destructive Devices
02 Pawnbroker in Firearms
03 Collector of Curios and Relics
06 Manufacturer of Ammunition
07 Manufacturer of Firearms Other Than Destructive Devices
08 Importer of Firearms Other Than Destructive Devices
09 Dealer in Destructive Devices
10 Manufacturer of Destructive Devices
11 Importer of Destructive Devices

Rate Limiting

  • Limit: 100 requests per minute per IP address
  • Burst: 10 requests allowed in burst
  • Response: HTTP 429 when limit exceeded
  • Headers: Rate limit information available in response headers

Error Handling

All error responses follow this format:

{
  "error": "Error message describing what went wrong"
}

Common HTTP status codes:

  • 200 - Success
  • 400 - Bad Request (invalid parameters)
  • 404 - Not Found (FFL not found)
  • 429 - Too Many Requests (rate limit exceeded)
  • 500 - Internal Server Error