Skip to content

Latest commit

 

History

History
1329 lines (1022 loc) · 25.4 KB

File metadata and controls

1329 lines (1022 loc) · 25.4 KB

API Documentation

Complete API reference for JudgeFinder Platform

Version: 1.0.0 Last Updated: 2025-10-26 Status: Production Base URL: https://judgefinder.io/api

Table of Contents

  1. API Overview
  2. Authentication
  3. Judge Endpoints
  4. Admin Endpoints
  5. Advertising Endpoints
  6. Billing Endpoints
  7. Other Endpoints
  8. Error Handling
  9. Rate Limiting
  10. Examples

API Overview

Base URLs

  • Production: https://judgefinder.io/api
  • Staging: https://staging.judgefinder.io/api
  • Development: http://localhost:3000/api

Versioning

API versioning strategy:

  • v1: Stable API (/api/v1/*)
  • Unversioned: Beta endpoints (/api/*)
  • Deprecation: 6-month notice before removal

Version Headers:

Accept: application/json
X-API-Version: 1

Response Format

All API responses follow a consistent structure:

Success Response:

{
  "data": { ... },
  "meta": {
    "timestamp": "2025-10-26T10:30:00Z",
    "version": "1.0.0"
  }
}

Paginated Response:

{
  "results": [...],
  "pagination": {
    "page": 1,
    "per_page": 20,
    "total_count": 1234,
    "total_pages": 62
  },
  "rate_limit_remaining": 95
}

Error Response:

{
  "error": "Resource not found",
  "code": "NOT_FOUND",
  "details": {
    "resource": "judge",
    "id": 123
  },
  "timestamp": "2025-10-26T10:30:00Z"
}

HTTP Status Codes

Code Meaning Description
200 OK Request succeeded
201 Created Resource created successfully
204 No Content Request succeeded, no response body
400 Bad Request Invalid request parameters
401 Unauthorized Authentication required
403 Forbidden Insufficient permissions
404 Not Found Resource not found
429 Too Many Requests Rate limit exceeded
500 Internal Server Error Server error occurred
503 Service Unavailable Service temporarily down

Authentication

Authentication Methods

JudgeFinder uses Clerk for authentication with multiple strategies:

  1. Session Authentication (Browser)
  2. API Key Authentication (Programmatic)
  3. Bearer Token Authentication (Mobile apps)

Session Authentication

For browser-based requests, Clerk handles authentication automatically via cookies:

// Client-side (automatic)
fetch('/api/judges/123', {
  credentials: 'include', // Include session cookie
})

API Key Authentication

For programmatic access:

# Generate API key in dashboard
# /dashboard/api-keys

# Use in requests
curl -H "Authorization: Bearer sk_live_abc123..." \
  https://judgefinder.io/api/v1/judges/123

API Key Permissions:

  • read:judges - Read judge data
  • read:analytics - Read analytics data
  • write:reports - Create reports
  • admin:all - Full admin access

Rate Limits by Authentication

User Type Search Limit API Limit Notes
Anonymous 10/day N/A IP-based
Authenticated 100/hour 1000/hour User-based
Subscriber 500/hour 5000/hour Account-based
API Key N/A 10000/hour Key-based

Headers

Required Headers:

Content-Type: application/json
Accept: application/json

Optional Headers:

Authorization: Bearer {token}
X-API-Key: {api-key}
X-Client-Version: 1.2.3
User-Agent: YourApp/1.0

Judge Endpoints

Search Judges

Search for judges by name, jurisdiction, or court.

Endpoint: GET /api/judges/search

Query Parameters:

Parameter Type Required Description
q string No Search query (name, court, jurisdiction)
limit integer No Results per page (1-500, default: 20)
page integer No Page number (default: 1)
jurisdiction string No Filter by jurisdiction (e.g., "CA", "NY")
court_type string No Filter by court type (e.g., "Superior", "Appellate")

Example Request:

curl "https://judgefinder.io/api/judges/search?q=smith&limit=10&page=1&jurisdiction=CA"

Example Response:

{
  "results": [
    {
      "id": 123,
      "name": "Judge John Smith",
      "court_name": "Superior Court of California, Los Angeles County",
      "jurisdiction": "CA",
      "total_cases": 1234,
      "slug": "judge-john-smith-123",
      "bio": "Judge Smith was appointed in 2015...",
      "appointment_date": "2015-06-01"
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 10,
    "total_count": 45,
    "total_pages": 5
  },
  "rate_limit_remaining": 95
}

Rate Limit:

  • Anonymous: 10 searches/day
  • Authenticated: 100 searches/hour

Caching: 60 seconds for search queries, 180 seconds for browse


Get Judge by ID

Retrieve detailed information about a specific judge.

Endpoint: GET /api/v1/judges/{id}

Path Parameters:

Parameter Type Required Description
id integer Yes Judge ID

Example Request:

curl "https://judgefinder.io/api/v1/judges/123" \
  -H "Authorization: Bearer {token}"

Example Response:

{
  "id": 123,
  "name": "Judge John Smith",
  "court_name": "Superior Court of California, Los Angeles County",
  "jurisdiction": "CA",
  "bio": "Judge Smith was appointed...",
  "appointment_date": "2015-06-01",
  "education": [
    {
      "degree": "JD",
      "institution": "UCLA School of Law",
      "year": 2000
    }
  ],
  "current_assignment": {
    "court_name": "Los Angeles Superior Court",
    "department": "Department 42",
    "assignment_type": "primary",
    "effective_date": "2020-01-01"
  },
  "analytics": {
    "total_cases": 1234,
    "settlement_rate": 0.65,
    "average_case_duration": 180,
    "bias_indicators": {
      "consistency_score": 85.5,
      "speed_score": 72.3,
      "settlement_preference": 15.2
    }
  },
  "sources": {
    "primary": "courtlistener",
    "verified": true,
    "last_updated": "2025-10-20T10:30:00Z"
  }
}

Rate Limit: 1000 requests/hour (authenticated)

Caching: 300 seconds (5 minutes)


Get Judge Analytics

Retrieve analytics for a specific judge.

Endpoint: GET /api/judges/{id}/analytics

Path Parameters:

Parameter Type Required Description
id integer Yes Judge ID

Query Parameters:

Parameter Type Required Description
period string No Time period ("30d", "90d", "1y", "all")
metrics string No Comma-separated metrics to include

Example Request:

curl "https://judgefinder.io/api/judges/123/analytics?period=90d" \
  -H "Authorization: Bearer {token}"

Example Response:

{
  "judge_id": 123,
  "period": "90d",
  "total_cases": 87,
  "outcomes": {
    "settled": 42,
    "dismissed": 20,
    "judgment": 25
  },
  "settlement_rate": 0.48,
  "average_case_duration": 156,
  "case_types": [
    {
      "type": "Civil",
      "count": 45,
      "settlement_rate": 0.62
    },
    {
      "type": "Criminal",
      "count": 30,
      "settlement_rate": 0.27
    }
  ],
  "bias_indicators": {
    "consistency_score": 85.5,
    "speed_score": 72.3,
    "settlement_preference": 15.2,
    "risk_tolerance": 60.1,
    "predictability_score": 78.9
  },
  "temporal_patterns": [
    {
      "year": 2025,
      "month": 7,
      "case_count": 32,
      "settlement_rate": 0.5
    }
  ]
}

Requires: Authentication + Subscription

Rate Limit: 100 requests/hour


Get Recent Cases

Retrieve recent cases for a judge.

Endpoint: GET /api/judges/{id}/recent-cases

Query Parameters:

Parameter Type Required Description
limit integer No Number of cases (1-50, default: 10)
offset integer No Pagination offset

Example Response:

{
  "cases": [
    {
      "id": 456,
      "case_number": "23-CV-12345",
      "case_type": "Civil",
      "filing_date": "2023-06-15",
      "decision_date": "2023-09-20",
      "outcome": "Settled",
      "parties": ["Smith v. Jones"],
      "case_value": 150000
    }
  ],
  "total_count": 1234,
  "has_more": true
}

Search by Slug

Get judge by URL-friendly slug.

Endpoint: GET /api/judges/by-slug

Query Parameters:

Parameter Type Required Description
slug string Yes Judge slug (e.g., "judge-john-smith-123")

Example Request:

curl "https://judgefinder.io/api/judges/by-slug?slug=judge-john-smith-123"

Example Response:

{
  "id": 123,
  "name": "Judge John Smith",
  "slug": "judge-john-smith-123"
  // ... (same as Get Judge by ID)
}

Export Judges

Export judge data in CSV or JSON format.

Endpoint: GET /api/v1/judges/export

Query Parameters:

Parameter Type Required Description
format string No Export format ("csv" or "json", default: "json")
jurisdiction string No Filter by jurisdiction
limit integer No Maximum records (1-10000)

Example Request:

curl "https://judgefinder.io/api/v1/judges/export?format=csv&jurisdiction=CA&limit=100" \
  -H "Authorization: Bearer {token}" \
  > judges.csv

Requires: Subscription

Rate Limit: 10 exports/hour


Admin Endpoints

Admin endpoints require admin authentication.

Get Sync Status

Check data synchronization status.

Endpoint: GET /api/admin/sync-status

Requires: Admin authentication

Example Response:

{
  "last_sync": "2025-10-26T02:00:00Z",
  "status": "completed",
  "synced_records": {
    "judges": 1543,
    "courts": 58,
    "cases": 45678
  },
  "errors": [],
  "next_scheduled_sync": "2025-10-27T02:00:00Z"
}

Get Database Stats

Retrieve database statistics.

Endpoint: GET /api/admin/stats

Example Response:

{
  "judges": {
    "total": 1543,
    "with_analytics": 1234,
    "recently_updated": 87
  },
  "cases": {
    "total": 456789,
    "last_30_days": 3456
  },
  "courts": {
    "total": 58,
    "active": 54
  },
  "users": {
    "total": 12345,
    "active_subscribers": 234,
    "trial_users": 45
  }
}

Trigger Manual Sync

Manually trigger data synchronization.

Endpoint: POST /api/admin/sync

Request Body:

{
  "sync_type": "judges",
  "jurisdiction": "CA",
  "force": false
}

Example Response:

{
  "sync_id": "sync_abc123",
  "status": "queued",
  "estimated_duration": 300,
  "message": "Sync job queued successfully"
}

Data Quality Report

Get data quality metrics.

Endpoint: GET /api/admin/data-quality

Example Response:

{
  "overall_score": 92.5,
  "metrics": {
    "completeness": 95.2,
    "accuracy": 89.8,
    "freshness": 92.5
  },
  "issues": [
    {
      "type": "missing_bio",
      "count": 87,
      "severity": "low"
    },
    {
      "type": "outdated_assignment",
      "count": 12,
      "severity": "medium"
    }
  ]
}

Advertising Endpoints

Endpoints for managing advertising campaigns.

Create Campaign

Create a new advertising campaign.

Endpoint: POST /api/advertising/campaigns

Request Body:

{
  "name": "My Campaign",
  "budget": 5000,
  "start_date": "2025-11-01",
  "end_date": "2025-11-30",
  "target_judges": [123, 456, 789],
  "ad_creative": {
    "headline": "Expert Legal Services",
    "description": "Contact us for consultation",
    "cta": "Learn More",
    "destination_url": "https://example.com"
  }
}

Example Response:

{
  "campaign_id": "camp_abc123",
  "status": "pending_approval",
  "estimated_impressions": 50000,
  "estimated_clicks": 2500,
  "cost_per_click": 2.0
}

Get Campaign Performance

Retrieve campaign performance metrics.

Endpoint: GET /api/advertising/campaigns/{id}/performance

Example Response:

{
  "campaign_id": "camp_abc123",
  "period": {
    "start": "2025-11-01",
    "end": "2025-11-30"
  },
  "metrics": {
    "impressions": 45678,
    "clicks": 2345,
    "click_through_rate": 0.051,
    "conversions": 123,
    "conversion_rate": 0.052,
    "cost": 4690.0,
    "cost_per_click": 2.0,
    "cost_per_conversion": 38.13
  },
  "top_performing_judges": [
    {
      "judge_id": 123,
      "impressions": 5678,
      "clicks": 345
    }
  ]
}

Track Ad Click

Track when user clicks an ad.

Endpoint: POST /api/advertising/track-click

Request Body:

{
  "campaign_id": "camp_abc123",
  "judge_id": 123,
  "session_id": "sess_xyz789"
}

Example Response:

{
  "tracked": true,
  "redirect_url": "https://example.com?utm_source=judgefinder"
}

Billing Endpoints

Endpoints for subscription management and billing.

Get Subscription

Retrieve current subscription details.

Endpoint: GET /api/billing/subscription

Requires: Authentication

Example Response:

{
  "subscription_id": "sub_abc123",
  "status": "active",
  "plan": {
    "id": "plan_monthly",
    "name": "Monthly Universal Access",
    "price": 500,
    "currency": "USD",
    "interval": "month"
  },
  "current_period": {
    "start": "2025-10-01",
    "end": "2025-11-01"
  },
  "cancel_at_period_end": false,
  "payment_method": {
    "type": "card",
    "last4": "4242",
    "exp_month": 12,
    "exp_year": 2026
  }
}

Update Subscription

Update or change subscription plan.

Endpoint: PUT /api/billing/subscription/update

Request Body:

{
  "new_plan_id": "plan_yearly",
  "proration_behavior": "create_prorations"
}

Example Response:

{
  "subscription_id": "sub_abc123",
  "status": "updated",
  "effective_date": "2025-11-01",
  "proration_amount": -417.0,
  "new_amount": 5000.0,
  "message": "Subscription updated successfully"
}

Cancel Subscription

Cancel subscription at end of billing period.

Endpoint: POST /api/billing/subscription/cancel

Request Body:

{
  "feedback": "Too expensive",
  "cancel_immediately": false
}

Example Response:

{
  "subscription_id": "sub_abc123",
  "status": "canceled",
  "cancel_at": "2025-11-01",
  "access_until": "2025-11-01",
  "refund_amount": 0
}

Get Invoices

Retrieve billing invoice history.

Endpoint: GET /api/billing/invoices

Query Parameters:

Parameter Type Required Description
limit integer No Number of invoices (1-100, default: 10)
starting_after string No Pagination cursor

Example Response:

{
  "invoices": [
    {
      "id": "in_abc123",
      "number": "INV-2025-001",
      "date": "2025-10-01",
      "amount_due": 500.0,
      "amount_paid": 500.0,
      "status": "paid",
      "pdf_url": "https://stripe.com/invoices/abc123.pdf"
    }
  ],
  "has_more": false
}

Create Stripe Portal Session

Create session for Stripe Customer Portal.

Endpoint: POST /api/stripe/create-portal

Example Response:

{
  "url": "https://billing.stripe.com/session/abc123"
}

Other Endpoints

Get Courts

Retrieve list of courts.

Endpoint: GET /api/courts

Query Parameters:

Parameter Type Required Description
jurisdiction string No Filter by jurisdiction
type string No Filter by court type

Example Response:

{
  "courts": [
    {
      "id": 1,
      "name": "Superior Court of California, Los Angeles County",
      "jurisdiction": "CA",
      "type": "Superior",
      "address": "111 N Hill St, Los Angeles, CA 90012",
      "judge_count": 234
    }
  ],
  "total_count": 58
}

Get Upcoming Elections

Retrieve upcoming judicial elections.

Endpoint: GET /api/elections/upcoming

Example Response:

{
  "elections": [
    {
      "id": 1,
      "date": "2026-06-05",
      "jurisdiction": "CA",
      "position": "Superior Court Judge, Seat 42",
      "candidates": [
        {
          "name": "John Smith",
          "party": "Non-partisan",
          "incumbent": true
        },
        {
          "name": "Jane Doe",
          "party": "Non-partisan",
          "incumbent": false
        }
      ]
    }
  ]
}

Health Check

Check API health status.

Endpoint: GET /api/health

Example Response:

{
  "status": "healthy",
  "version": "1.0.0",
  "timestamp": "2025-10-26T10:30:00Z",
  "services": {
    "database": "healthy",
    "cache": "healthy",
    "ai": "healthy"
  }
}

AI Chat Search

AI-powered conversational judge search.

Endpoint: POST /api/chat

Request Body:

{
  "message": "Find judges in Los Angeles who handle civil cases and have high settlement rates",
  "context": {
    "previous_messages": []
  }
}

Example Response:

{
  "response": "I found 12 judges in Los Angeles Superior Court who specialize in civil cases with settlement rates above 60%. Here are the top 3...",
  "judges": [
    {
      "id": 123,
      "name": "Judge John Smith",
      "relevance_score": 0.95
    }
  ],
  "follow_up_questions": [
    "Would you like to see more details about Judge Smith?",
    "Should I filter by specific case value ranges?"
  ]
}

Error Handling

Error Response Format

{
  "error": "Human-readable error message",
  "code": "ERROR_CODE",
  "details": {
    "field": "Additional context"
  },
  "timestamp": "2025-10-26T10:30:00Z",
  "request_id": "req_abc123"
}

Common Error Codes

Code HTTP Status Description
INVALID_REQUEST 400 Invalid request parameters
UNAUTHORIZED 401 Authentication required
FORBIDDEN 403 Insufficient permissions
NOT_FOUND 404 Resource not found
VALIDATION_ERROR 422 Request validation failed
RATE_LIMIT_EXCEEDED 429 Too many requests
INTERNAL_ERROR 500 Internal server error
SERVICE_UNAVAILABLE 503 Service temporarily down

Error Examples

Invalid Request:

{
  "error": "Limit cannot exceed 500",
  "code": "INVALID_REQUEST",
  "details": {
    "parameter": "limit",
    "provided": 1000,
    "maximum": 500
  }
}

Authentication Required:

{
  "error": "Authentication required",
  "code": "UNAUTHORIZED",
  "details": {
    "message": "This endpoint requires authentication. Please include an Authorization header."
  }
}

Rate Limit Exceeded:

{
  "error": "Rate limit exceeded",
  "code": "RATE_LIMIT_EXCEEDED",
  "details": {
    "limit": 100,
    "remaining": 0,
    "reset": 1730000000,
    "reset_in_seconds": 3600
  }
}

Rate Limiting

Rate Limit Headers

All responses include rate limit information:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1730000000

Rate Limit Tiers

Endpoint Anonymous Authenticated Subscriber API Key
Search 10/day 100/hour 500/hour N/A
Get Judge N/A 1000/hour 5000/hour 10000/hour
Analytics N/A N/A 100/hour 1000/hour
Export N/A N/A 10/hour 100/hour

Handling Rate Limits

Best Practices:

  1. Check X-RateLimit-Remaining header
  2. Implement exponential backoff
  3. Cache responses when possible
  4. Use webhooks instead of polling

Example Implementation:

async function fetchWithRateLimit(url: string) {
  const response = await fetch(url)

  const remaining = parseInt(response.headers.get('X-RateLimit-Remaining') || '0')

  if (remaining < 10) {
    console.warn('Approaching rate limit')
  }

  if (response.status === 429) {
    const reset = parseInt(response.headers.get('X-RateLimit-Reset') || '0')
    const waitTime = reset - Date.now() / 1000

    await sleep(waitTime * 1000)
    return fetchWithRateLimit(url) // Retry
  }

  return response.json()
}

Examples

cURL Examples

Search Judges:

curl -X GET "https://judgefinder.io/api/judges/search?q=smith&jurisdiction=CA" \
  -H "Accept: application/json"

Get Judge with Authentication:

curl -X GET "https://judgefinder.io/api/v1/judges/123" \
  -H "Authorization: Bearer sk_live_abc123..." \
  -H "Accept: application/json"

Create Campaign:

curl -X POST "https://judgefinder.io/api/advertising/campaigns" \
  -H "Authorization: Bearer sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Campaign",
    "budget": 5000,
    "target_judges": [123, 456]
  }'

JavaScript Examples

Using Fetch API:

// Search judges
async function searchJudges(query) {
  const response = await fetch(
    `https://judgefinder.io/api/judges/search?q=${encodeURIComponent(query)}`,
    {
      headers: {
        Accept: 'application/json',
      },
    }
  )

  if (!response.ok) {
    throw new Error(`HTTP error ${response.status}`)
  }

  return await response.json()
}

// Get judge with auth
async function getJudge(id, apiKey) {
  const response = await fetch(`https://judgefinder.io/api/v1/judges/${id}`, {
    headers: {
      Authorization: `Bearer ${apiKey}`,
      Accept: 'application/json',
    },
  })

  return await response.json()
}

TypeScript Examples

With Type Safety:

interface Judge {
  id: number
  name: string
  court_name: string
  jurisdiction: string
  total_cases: number
}

interface SearchResponse {
  results: Judge[]
  pagination: {
    page: number
    per_page: number
    total_count: number
  }
  rate_limit_remaining: number
}

async function searchJudges(query: string): Promise<SearchResponse> {
  const response = await fetch(
    `https://judgefinder.io/api/judges/search?q=${encodeURIComponent(query)}`,
    {
      headers: {
        Accept: 'application/json',
      },
    }
  )

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`)
  }

  return await response.json()
}

Python Examples

Using Requests:

import requests

# Search judges
def search_judges(query, limit=20):
    response = requests.get(
        'https://judgefinder.io/api/judges/search',
        params={'q': query, 'limit': limit},
        headers={'Accept': 'application/json'}
    )
    response.raise_for_status()
    return response.json()

# Get judge with authentication
def get_judge(judge_id, api_key):
    response = requests.get(
        f'https://judgefinder.io/api/v1/judges/{judge_id}',
        headers={
            'Authorization': f'Bearer {api_key}',
            'Accept': 'application/json'
        }
    )
    response.raise_for_status()
    return response.json()

# Example usage
results = search_judges('smith', limit=10)
for judge in results['results']:
    print(f"{judge['name']} - {judge['court_name']}")

Webhooks

Stripe Webhooks

Stripe events are received via webhook:

Endpoint: POST /api/webhooks/stripe

Events Handled:

  • invoice.paid - Invoice successfully paid
  • invoice.payment_failed - Payment failed
  • customer.subscription.created - Subscription created
  • customer.subscription.updated - Subscription updated
  • customer.subscription.deleted - Subscription canceled

Webhook Security:

// Verify webhook signature
import Stripe from 'stripe'

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)

export async function POST(request: Request) {
  const signature = request.headers.get('stripe-signature')!
  const body = await request.text()

  try {
    const event = stripe.webhooks.constructEvent(
      body,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET!
    )

    // Handle event
    switch (event.type) {
      case 'invoice.paid':
        await handleInvoicePaid(event.data.object)
        break
      // ...
    }

    return new Response('OK', { status: 200 })
  } catch (error) {
    return new Response('Webhook signature invalid', { status: 400 })
  }
}

CourtListener Webhooks

Endpoint: POST /api/webhooks/courtlistener

Events:

  • opinion.created - New opinion filed
  • docket.updated - Docket updated
  • judge.updated - Judge information updated

Related Documentation


Last Updated: 2025-10-26 Maintained By: JudgeFinder Development Team

Need Help? Contact support@judgefinder.io