Build a shelf and ATM capacity monitor for small-cap stocks

Build a Python monitor for small-cap shelf registrations and ATM capacity with AskEdgar’s API, baby-shelf fields, filing links, and nightly updates.

Use AskEdgar's Registrations API to review effective shelves and ATM programs for a ticker, with remaining ATM capacity, baby-shelf fields, update dates, and source filings. This tutorial produces two research views you can add to a watchlist or AI-assisted dashboard.

What the monitor answers

  • Which shelf registrations and ATM programs are marked effective?
  • What remaining ATM capacity and baby-shelf figures are available?
  • When were the records updated, and which filings support them?

Capacity is not a forecast of an offering. Keep cash need, completed financing activity, and instrument terms alongside these records when assessing a company.

Start with one ticker and a request budget

Complete the API Quickstart first. Save your API key as the server-side environment variable ASKEDGAR_API_KEY.

Use the free usage estimator before fetching data. The estimator supports a subset of query parameters, so a broad estimate is not a guarantee of the exact cost of these filtered requests. Record limits and pagination affect usage.

The example below uses Python 3 and its standard library: no extra packages are required. It makes two separate paginated queries and permits at most five pages of 50 records per query. That is a maximum of ten requests, not a flat dollar budget. If a query still has more results, the program stops with an error instead of printing a partial monitor. Tighten your query or deliberately adjust the page budget after checking costs.

Request effective shelves and ATM programs

The first query uses type=SHELF&effective_status=true. The second uses is_atm=true&effective_status=true. Save the following code as shelf_capacity_monitor.py, set your API key in the environment, then run python shelf_capacity_monitor.py YOUR_TICKER.

import json
import os
import warnings
from urllib.parse import urlencode
from urllib.request import Request, urlopen

BASE_URL = "https://eapi.askedgar.io"


def fetch_page(path, params):
    key = os.environ["ASKEDGAR_API_KEY"]
    request = Request(
        f"{BASE_URL}{path}?{urlencode(params)}",
        headers={"API-KEY": key},
    )
    # HTTP errors, including insufficient credits or rate limits, stop the run.
    with urlopen(request, timeout=30) as response:
        return json.load(response)


def get_records(path, filters, max_pages=20):
    records = []
    for page in range(1, max_pages + 1):
        payload = fetch_page(path, {**filters, "page": page, "limit": 50})
        if payload.get("status") != "success":
            raise RuntimeError(payload.get("message") or "AskEdgar request failed")
        if payload.get("message"):
            warnings.warn(str(payload["message"]))
        rows = payload.get("results")
        if not isinstance(rows, list):
            raise ValueError("Expected a results list")
        records.extend(rows)
        if payload.get("has_more") is False:
            return records
        if payload.get("has_more") is not True or not rows:
            raise ValueError("Unexpected pagination; refusing partial results")
    raise RuntimeError("Page budget reached. Tighten filters or raise max_pages deliberately.")


def run_monitor(ticker):
    queries = {
        "Effective shelf registrations": {"ticker": ticker, "type": "SHELF", "effective_status": "true"},
        "Effective ATM programs": {"ticker": ticker, "is_atm": "true", "effective_status": "true"},
    }
    fields = [
        "ticker", "headline", "file_no", "effective_status", "effective_date",
        "expiration_date", "last_updated", "offering_amount", "total_raised",
        "amount_sold_12_mos", "baby_shelf_raisable_amount", "over_baby_shelf",
        "is_atm", "amount_remaining_atm", "bank", "details", "document_url", "askedgar_url",
    ]
    output = {}
    for label, filters in queries.items():
        rows = get_records("/v1/registrations", filters, max_pages=5)
        output[label] = [{field: row.get(field) for field in fields} for row in rows]
    # Preserve separate views: a program can overlap a shelf's capacity.
    # JSON null means unavailable; it must not be changed to zero.
    print(json.dumps(output, indent=2))


if __name__ == "__main__":
    import sys
    if len(sys.argv) != 2 or not sys.argv[1].strip():
        raise SystemExit("Usage: python shelf_capacity_monitor.py TICKER")
    run_monitor(sys.argv[1].strip().upper())

The output retains the API's values and nulls. An empty list means no matching records were returned for that query. It does not establish that the company has no other financing options. HTTP errors, API error statuses, and unexpected pagination stop the run.

Read the amounts in context

FieldHow to use it
offering_amountReview the registered/program amount in the context of the transaction; do not label it remaining capacity.
amount_remaining_atmDisplay as remaining ATM capacity when provided. Keep the program, filing, and update date attached.
baby_shelf_raisable_amountThe response field for what the company can currently raise after accounting for applicable baby-shelf limits and prior sales; inspect the source and related restrictions.
amount_sold_12_mosReview prior reported sales alongside capacity calculations.
total_raisedKeep the amount raised distinct from amounts still available.
effective_status, effective_date, expiration_dateDisplay together; do not equate an effective registration with imminent issuance.
file_no, details, document_url, askedgar_url, last_updatedRetain the registration identity, transaction context, evidence, and freshness.

The response field formerly described as calculated_raisable_amount is returned as baby_shelf_raisable_amount. The documented query filters retain the names min_calculated_raisable_amount and max_calculated_raisable_amount; use the response name when reading JSON. The example above already uses the current response field.

AskEdgar documents nightly updates to remaining shelf/ATM capacity and baby-shelf calculations. A nightly update is not an intraday execution feed. Preserve last_updated, review newer filings when relevant, and distinguish each record's dates from the time your app fetched it.

Do not add shelf and ATM amounts into a ticker-level total: an ATM can draw on a related shelf. Inspect the filing numbers and transaction details together. Multiple rows can also represent related filings or amendments. Keep them visible until you have reconciled the financing structure.

Baby-shelf eligibility and capacity depend on regulatory definitions and conditions. Ordinary stock market capitalization or a scanner's tradable-share float is not a substitute for the applicable regulatory public-float measure. This example displays the supplied fields; it does not implement its own legal eligibility calculator. See the SEC's Securities Act Forms guidance and the issuer's filings for context.

Turn the output into a useful dashboard

Create one row per returned record. Show the headline, filing number, effective and expiration dates, updated time, relevant capacity fields, and source links. Display missing values as “Unavailable.” Preserve zero when the API actually returns zero.

Store a timestamped copy of each fetch. A change between snapshots should trigger a review of the updated record and filing; it does not by itself prove the company sold shares. Do not merge or sum records just because their tickers match.

Pair this view with Dilution Rating for cash need and offering-ability context, and Offerings for reported financings and ATM sales. Review warrants and convertible securities through the dilution datasets when researching other potential share supply.

Build it with an AI coding assistant

Build a server-side small-cap financing-capacity monitor using AskEdgar's Registrations API and the Python example above. Keep effective shelves and ATM programs in separate views. Show filing numbers, dates, remaining ATM capacity, baby-shelf fields, missing values, and links to the supporting documents. Do not add shelf and ATM capacity together or predict offering dates. Keep the API key in a server-side secret, cache repeated ticker requests, and enforce a page budget.

Start with the working dashboard path in the Quickstart, or use the low-float scanner tutorial to choose a watchlist before adding financing-capacity detail.


Did this page help you?