Skip to content
Lottery Feed API

Lottery Feed API

ResultsPersonalAPI DocsPricingContact
Sign inJoin the Free Beta
  1. Home
  2. /
  3. API docs
  4. /
  5. SDK and Codegen Starter

API documentation

SDK and Codegen Starter

Build a small server-side Lottery Feed API client from the OpenAPI contract without exposing bearer tokens.

Section

05 / 27

OpenAPI JSON
01Introduction02Quickstart03Polling and integration04Sandbox fixtures05SDK starter06Embeddable widgets07API playground08OpenAPI spec09Postman collection10API changelog11Authentication12API key scopes13Freshness levels14Source confidence15Status monitoring16Rate limiting17Get countries18Get states19Get lotteries20Get lottery21Get schedules22Draw years23Lottery results24History export25Number frequency26Result checker27Error handling

Live API examples

Jump from the docs to real public pages that use the same read models.

Latest results browserSee the compact public result model with filters and draw dates.
Lottery catalogFind game names, states, ball counts, latest numbers, and API guides.
Maryland Pick 3 API guideOpen a concrete endpoint example using the same game shown in snippets.
Pricing and limitsCompare freshness targets, request limits, and integration features.
Customer integration centerSign in to check API keys, usage, webhook delivery, plan limits, and the next recommended action.
OpenAPI contractUse the machine-readable contract for code generation and integration tests.

Endpoints

GET
/openapi.json

Public OpenAPI 3.1 contract for code generation.

GET
/postman-collection.json

Postman collection generated from the same public contract.

GET
/sdk-examples.json

Cacheable starter clients and sandbox smoke examples for SDK onboarding.

GET
/v1/results/latest

Latest compact result rows used by SDK smoke tests.

POST
/v1/results/check

Checker response used by SDK POST smoke tests.

GET
/v1/results/history

Date-filtered history rows used by customer backfills.

Start from the public contract

Use /openapi.json as the source of truth for generated clients, integration tests, and typed DTOs. The contract describes the customer-facing /v1 routes and their authentication, validation, response, and error semantics.

curl -sS "https://lotteryfeedapi.com/openapi.json" -o lottery-feed-openapi.json

Machine-readable starter examples

Use /sdk-examples.json for copy-ready server-side starter clients, deterministic sandbox smoke tests, public contract links, and the version policy in one cacheable JSON file. It is noindex, generated from the public API contract, and intentionally excludes live bearer keys and private implementation details.

curl -sS "https://lotteryfeedapi.com/sdk-examples.json" -o lottery-feed-sdk-examples.json

Minimal TypeScript client

Keep this wrapper on the server side. It uses explicit timeouts, sends bearer tokens only through headers, returns the compact JSON model that public endpoints document, and turns 401/403/429 bodies into a typed LotteryFeedApiError for customer logs and backoff decisions.

type LotteryFeedClientOptions = {
  apiKey: string;
  baseUrl?: string;
  timeoutMs?: number;
};

type LatestResultRow = {
  game_code: string;
  draw_date: string;
  numbers_formatted?: string;
  numbers_string?: string;
};

type LatestResultsResponse = {
  data?: LatestResultRow[];
};

type CheckResultResponse = {
  matched: boolean;
  match_class: string;
  submitted_numbers_formatted: string;
  winning_numbers_formatted: string;
};

type LotteryFeedRateLimit = {
  scope: 'api_key' | 'workspace' | 'sandbox_client';
  window: 'utc_day' | 'utc_month' | 'sliding_window';
  limit: number;
  used?: number;
  remaining: number;
  reset_at?: string;
  retry_after_seconds: number;
  hint: string;
};

type LotteryFeedErrorPayload = {
  error?: {
    code?: string;
    message?: string;
    rate_limit?: LotteryFeedRateLimit;
  };
};

export class LotteryFeedApiError extends Error {
  readonly status: number;
  readonly code: string;
  readonly endpoint: string;
  readonly rateLimit?: LotteryFeedRateLimit;
  readonly retryAfterSeconds?: number;

  constructor(input: {
    status: number;
    code: string;
    message: string;
    endpoint: string;
    rateLimit?: LotteryFeedRateLimit;
    retryAfterSeconds?: number;
  }) {
    super('Lottery Feed API ' + input.status + ' ' + input.code + ': ' + input.message);
    this.name = 'LotteryFeedApiError';
    this.status = input.status;
    this.code = input.code;
    this.endpoint = input.endpoint;
    this.rateLimit = input.rateLimit;
    this.retryAfterSeconds = input.retryAfterSeconds;
  }
}

export function createLotteryFeedClient({
  apiKey,
  baseUrl = 'https://api.lotteryfeedapi.com/v1',
  timeoutMs = 10000
}: LotteryFeedClientOptions) {
  async function request<T>(
    path: string,
    options: {
      method?: 'GET' | 'POST';
      params?: Record<string, string | number | undefined>;
      body?: unknown;
      authenticated?: boolean;
    } = {}
  ) {
    const method = options.method || 'GET';
    const url = new URL(path.replace(/^\/+/, ''), baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`);
    for (const [key, value] of Object.entries(options.params || {})) {
      if (value !== undefined && value !== '') url.searchParams.set(key, String(value));
    }

    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), timeoutMs);

    try {
      const response = await fetch(url, {
        method,
        headers: {
          accept: 'application/json',
          ...(options.authenticated === false ? {} : { authorization: `Bearer ${apiKey}` }),
          ...(options.body === undefined ? {} : { 'content-type': 'application/json' })
        },
        body: options.body === undefined ? undefined : JSON.stringify(options.body),
        signal: controller.signal
      });

      if (!response.ok) {
        const payload = (await response.json().catch(() => null)) as LotteryFeedErrorPayload | null;
        const retryAfterSeconds = parseRetryAfter(response.headers.get('retry-after'));
        throw new LotteryFeedApiError({
          status: response.status,
          code: payload?.error?.code || 'HTTP_' + response.status,
          message: payload?.error?.message || response.statusText || 'Request failed.',
          endpoint: url.pathname,
          rateLimit: payload?.error?.rate_limit,
          retryAfterSeconds: retryAfterSeconds ?? payload?.error?.rate_limit?.retry_after_seconds
        });
      }

      return (await response.json()) as T;
    } finally {
      clearTimeout(timeout);
    }
  }

  return {
    latest: (params?: { state?: string; ball_count?: number; game_code?: string; limit?: number }) =>
      request<LatestResultsResponse>('/results/latest', { params }),
    history: (params: { date: string; state?: string; game_code?: string; ball_count?: number; limit?: number }) =>
      request('/results/history', { params }),
    check: (body: { game_code: string; draw_date: string; numbers: string[] }) =>
      request<CheckResultResponse>('/results/check', { method: 'POST', body }),
    lotteries: (params?: { state?: string; ball_count?: number; limit?: number }) => request('/lotteries', { params }),
    status: () => request('/status', { authenticated: false })
  };
}

function parseRetryAfter(value: string | null) {
  if (!value) return undefined;
  const seconds = Number(value);
  return Number.isFinite(seconds) && seconds > 0 ? seconds : undefined;
}

const client = createLotteryFeedClient({
  apiKey: process.env.LOTTERY_FEED_API_KEY || 'lf_test_demo_sandbox'
});

try {
  const latest = await client.latest({ state: 'MD', ball_count: 3, limit: 1 });
  console.log(latest.data?.[0]?.numbers_formatted);

  const check = await client.check({ game_code: 'md-pick3-midday', draw_date: '2026-06-21', numbers: ['2', '3', '6'] });
  console.log(check.match_class, check.winning_numbers_formatted);
} catch (error) {
  if (error instanceof LotteryFeedApiError && error.code === 'RATE_LIMIT_EXCEEDED') {
    console.warn('Pause this key for', error.retryAfterSeconds, 'seconds', error.rateLimit);
  }
  throw error;
}

Rate-limit aware errors

Generated clients should preserve error.code and error.message on every failure, plus error.rate_limit when the response provides it. Quota 429 responses and rate-limit enforcement 503 responses include rate-limit evidence; FEED_SNAPSHOT_UNAVAILABLE instead returns its stable code, message, request_id, and Retry-After. Map 401 to key rotation or credential setup, 403 to scope or plan correction, RATE_LIMIT_EXCEEDED to pause the affected key until Retry-After or error.rate_limit.reset_at, RATE_LIMIT_UNAVAILABLE to temporary enforcement unavailability, and FEED_SNAPSHOT_UNAVAILABLE to a bounded retry after the result-data snapshot recovers. The compact error object is safe to log; Authorization headers and raw lf_live keys are not.

try {
  const latest = await client.latest({ state: 'MD', ball_count: 3, limit: 1 });
  console.log(latest.data?.[0]?.numbers_formatted);
} catch (error) {
  if (error instanceof LotteryFeedApiError && error.code === 'RATE_LIMIT_EXCEEDED') {
    const delaySeconds = error.retryAfterSeconds ?? error.rateLimit?.retry_after_seconds ?? 60;
    console.warn('Pause this key before retrying', {
      delaySeconds,
      endpoint: error.endpoint,
      rate_limit: error.rateLimit
    });
  }
  throw error;
}

Generated clients

Generate clients from the public OpenAPI document in your own CI and keep bearer-token clients behind your server layer. Generated clients should preserve the shared 400, 401, 403, 429, 405, and 503 error envelope and should run deterministic sandbox smoke tests before release.

# Example CI shape:
curl -sS "https://lotteryfeedapi.com/openapi.json" -o lottery-feed-openapi.json
openapi-generator-cli generate -i lottery-feed-openapi.json -g typescript-fetch -o ./generated/lottery-feed-typescript
openapi-generator-cli generate -i lottery-feed-openapi.json -g python -o ./generated/lottery-feed-python
openapi-generator-cli generate -i lottery-feed-openapi.json -g php -o ./generated/lottery-feed-php
# run deterministic sandbox contract tests before publishing your client

Sandbox contract test

Use sandbox keys to verify client wiring without waiting for live draws. Sandbox requests are deterministic, do not read the live feed, and do not count as production usage.

const client = createLotteryFeedClient({
  apiKey: 'lf_test_demo_sandbox'
});

const latest = await client.latest({ state: 'MD', ball_count: 3 });
if (latest.data?.[0]?.numbers_formatted !== '2-3-6') {
  throw new Error('Sandbox contract changed or client mapping failed.');
}

const check = await client.check({
  game_code: 'md-pick3-midday',
  draw_date: '2026-06-21',
  numbers: ['2', '3', '6']
});
if (check.match_class !== 'exact_order' || check.winning_numbers_formatted !== '2-3-6') {
  throw new Error('Sandbox checker contract changed or POST mapping failed.');
}

Operational guardrails

Use idempotent retries only for GET requests, respect HTTP 429 Retry-After and RateLimit headers, store draw_date and numbers_formatted in your own system, and log response status plus endpoint path without logging bearer tokens. For high-traffic polling, prefer feed links or cached latest-result snapshots where the freshness tier allows it.

SDK integration FAQ

Short answers for teams generating clients and testing server-side Lottery Feed API integrations.

Can I ship the generated SDK in a browser app?

Only if it never contains a bearer API key. Browser embeds should use widgets or revocable feed links. Server-side applications should keep lf_live and lf_test bearer tokens in server-side secrets.

How often should generated clients be refreshed?

Refresh clients after OpenAPI or changelog updates, then run sandbox contract tests before publishing the generated package to your application.


Next: Embeddable widgets
Lottery Feed API

Normalized lottery draw results, history, schedules, source-confidence metadata, and descriptive analytics for integrations and personal analysis.

Independent software and data service. We do not sell lottery tickets, accept bets or player funds, operate draws, determine outcomes or pay prizes.Lottery names and trademarks belong to their respective owners. Lottery Feed API is an independent data service and is not affiliated with, endorsed by, sponsored by or operated by any lottery authority.
LotteriesDaily ArchiveCountriesStatesStatusResponsible UseAcceptable UseReport incorrect resultCompanyTermsPrivacyAccount