> ## Documentation Index
> Fetch the complete documentation index at: https://octo-98895abd.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Securities lending

> B3's securities-lending book: short interest and % of float, the sector aggregate, the trade tape and the brokerage on each leg.

<Note>
  **Parameters, response columns and types are generated** from the SQL catalog and
  render under **Endpoints → Securities lending** in the sidebar. This page carries what a
  schema cannot: the conventions, the nulls that are deliberate, and the traps.
</Note>

### Overview

Four views over B3's securities-lending market: what is on loan, at what rate, and who traded it. They are the closest thing the Brazilian market publishes to a short interest report.

| Endpoint | Grain | What it holds |
| - | - | - |
| `GET /short_interest` | `(ticker, trade_date)` | Balance on loan, % of float with the basis that produced it, days to cover, lending rates |
| `GET /short_interest_by_sector` | `(sector, trade_date)` | The same book aggregated by B3 top-level sector |
| `GET /lending_trades` | `(ticker, trade_date)` | The session's trade tape per ticker: count, quantity, quantity-weighted rate |
| `GET /lending_participants` | `(ticker, trade_date, broker_code)` | What each **brokerage** lent and borrowed |

These are **views**: `GET`, PostgREST filters (`?ticker=eq.PETR4`), `limit`/`offset` paging. Investor-type flow is on [Investor flow](/api-docs/flows).

<Warning>
  **History starts when SILO started capturing, and can never be extended backwards.** B3 keeps roughly **21 business days** of these tables and publishes no archive. This is the only part of the warehouse that behaves this way.

  * **A short window is the source's retention, not a data problem.** Do not read the first date as a market event, a listing date, or a bug.
  * **No long-run average, 52-week range or year-over-year change** until SILO has held that long. A ratio against a "historical norm" with three weeks behind it describes the capture window, not the market.

  An over-wide request to B3 returns HTTP 200 with a silently clamped window, so every ingest reconciles the sessions it received against the ones it asked for.
</Warning>

**Sorting on a nullable column:** nulls sort **last** by default in PostgREST. `pct_float` and `days_to_cover` are null whenever their denominator is missing, so `order=pct_float.desc` buries those rows at the end rather than dropping them. Add `.nullslast` explicitly if you rely on that placement.

### `GET /short_interest`

Short interest per `(ticker, trade_date)`, built from B3's own `Total` rows so balances are never double-counted. Filters: `ticker`, `trade_date` (sessions only, no weekend rows), `float_basis` (**required for any ranking**), `instrument_category` (`SHARES`, `UNIT`, `ETF EQUITIES`, `BDR`, `FUNDS`). An unfiltered days-to-cover screen is dominated by index ETFs, not the crowded single-name shorts the screen is for.

<Warning>
  **Read `pct_float` together with `float_basis`.** Without it you get a confident, wrong ranking.

  | `float_basis` | Denominator | Coverage | Reads |
  | - | - | - | - |
  | `index_free_float` | B3's published free float (`theoretical_qty`) from the broadest index portfolio carrying the ticker | Index constituents only, \~149 tickers | The free-float percentage the market quotes |
  | `shares_outstanding` | Capital social from the cash instrument registry | Everything else with a registry entry | A **smaller** percentage for the same position |

  They are never comparable. **Any ranking, screen or cross-section must filter to one basis first**; the free-float basis gives systematically larger percentages, so an unfiltered "most shorted" list is really a list of index constituents.

  `float_denominator` carries the number actually used. Index priority is IBRA → SMLL → IBXX → IBOV: weight caps can only shrink the quantity below the real float, so the broadest index is the closest published estimate.
</Warning>

| Field | What to know |
| - | - |
| `pct_float` / `days_to_cover` | `null`, never `0`, when their denominator is missing. `0%` would read as "nobody is short", and a short in a name that has not traded is *uncoverable*. Filter nulls out deliberately. |
| `sector_top` | B3's top-level sector with spelling variants merged (`Bens Indls` and `Bens Industriais` are one). No ticker is moved between sectors; an unrecognised prefix passes through verbatim. Null outside the index universe. |
| `short_quantity` | Shares on loan, summed across specifications (a ticker can carry `ON` and `ON NM`). |
| `days_to_cover` | `short_quantity / adtv_qty_21`: sessions of average volume needed to buy the position back. |
| `adtv_value_21` | Trailing 21-session average traded value in BRL, computed per short-interest date (a row dated three weeks ago uses the volume that preceded *it*). |
| `adtv_sessions` | Sessions the average covers. Fewer than 21 (early in a listing, or after a halt) means a shorter, noisier window. |
| `lender_rate_pct` / `borrower_rate_pct` | Quantity-weighted rates, percentage points a.a. Markets with no registered quantity are excluded. |

Example, the most shorted index names in a session (the `float_basis` filter is not optional):

```bash curl theme={null}
curl -s -G "$SILO_URL/short_interest" \
  -H "apikey: $SILO_ANON_KEY" \
  --data-urlencode "select=ticker,trade_date,pct_float,float_basis,days_to_cover,short_value" \
  --data-urlencode "trade_date=eq.2026-09-16" \
  --data-urlencode "float_basis=eq.index_free_float" \
  --data-urlencode "instrument_category=eq.SHARES" \
  --data-urlencode "order=pct_float.desc" \
  --data-urlencode "limit=25"
```

Check `coverage` for the newest session before pinning a `trade_date`.

### `GET /short_interest_by_sector`

One row per `(sector, trade_date)`. Filters: `trade_date`, `sector`. `short_value` is the sector total; `short_value_equities` restricts it to `SHARES` and `UNIT`; `tickers` counts what went into the bucket.

**Note**: `Não classificado` is a real bucket. Sector comes from the B3 index portfolios, so ETFs, BDRs and every off-index name land there (bucketed, not dropped). For single-name risk read `short_value_equities`; the unclassified bucket is almost entirely ETFs and BDRs.

### `GET /lending_trades`

The session's lending trades, aggregated per `(ticker, trade_date)`.

| Field | What to know |
| - | - |
| `rate_pct` | **Quantity-weighted** average annualized rate, percentage points. |
| `borrower_brokers` / `lender_brokers` | Distinct **brokerages** on each leg, not distinct borrowers or lenders. |
| `internal_trades` | Trades where the same broker code stood on both legs. Usually most of them. |

**Note**: B3's own published average weights by the **number of trades**; `rate_pct` weights by **quantity**. Both are right. Measured 2026-09-10 across the 569 tickers both cover: mean absolute difference **0.037 percentage points**, 561 of 569 within 0.5pp, largest gap 3.81pp.

### `GET /lending_participants`

One row per `(ticker, trade_date, broker_code)`: what each brokerage lent and borrowed.

<Warning>
  **`doador` and `tomador` are BROKERAGES, not beneficial owners.** Reading them as owners produces a confident, wrong story about who is short.

  B3 names about **33 participants** in a whole session, and on 2026-09-10 **32,197 of 43,165 trades (74.6%)** carried the **same code on both legs**: a broker crossing its own client book. A large `quantity_borrowed` through a broker is its *clients'* position, not a view the broker took.

  **`internal_legs` and `internal_qty` tell the two apart.** A high internal share is client churn; a low one is flow that actually crossed the market. That session, J.P. Morgan sat at \~6% internal (directional) while Morgan Stanley was at \~78% (client churn).

  The split is published rather than netted away: dropping self-crossed trades makes the rest look like inter-broker conviction, keeping them makes churn look like directional demand. The choice is yours.
</Warning>

| Field | What to know |
| - | - |
| `broker_code` | B3 participant code, part of the grain. **The intermediary, never the owner.** |
| `quantity_net` | `quantity_lent - quantity_borrowed`. A trade crossed internally counts on **both** sides, so it cancels here. |
| `internal_qty` | Quantity on the legs where this broker was on both sides. Compare with `quantity_lent + quantity_borrowed` for the internal share. |

```python Python theme={null}
params = {"ticker": "eq.PETR4", "trade_date": "eq.2026-09-16",
          "order": "quantity_borrowed.desc", "limit": 10}
rows = requests.get(f"{URL}/lending_participants", headers=H, params=params).json()
for r in rows:  # directional borrow vs a broker's own client churn
    gross = (r["quantity_lent"] or 0) + (r["quantity_borrowed"] or 0)
    print(r["broker_name"], r["quantity_borrowed"], (r["internal_qty"] or 0) / gross if gross else None)
```

### Paging and empty results

These are views: the 1,000-row page is PostgREST's `db-max-rows` and it **cuts silently with a `200`**, keeping the oldest rows. It does not raise like the RPC functions.

* Send `Prefer: count=exact` and read `Content-Range`: `0-999/*` means the response was cut; `0-999/4382` gives the true total.
* Page with `limit` / `offset` and an explicit `order`; without a total order a page boundary can duplicate one row and drop another.
* The [SDK](/api-docs/sdk): `silo.short_interest(...)` returns one page and raises `SiloTruncated` on an unbounded cut; `silo.view_all("short_interest", order="trade_date.desc")` walks every page.
* A whole session of `lending_participants` is over one page (one row per `(ticker, broker)`): filter by `ticker` or page it.

Unknown ticker, or a known ticker with no rows in the window, is `200 []`, never a fabricated balance. With \~21 business days of retention an empty window is common: check [`coverage`](/api-docs/coverage) first. Landing tables are not readable by client roles; these views are owner-privileged, so the columns they select are the whole boundary.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.