---
title: "Token locks & vesting feed — newest lock contracts across all tokens (PRO+)"
method: GET
path: "/rhc/tokens/locks"
tags: ["Robinhood Chain"]
---

# Token locks & vesting feed — newest lock contracts across all tokens (PRO+)

`GET /rhc/tokens/locks`

## Request

- Base URL: `https://madeonsol.com/api/v1`
- URL: `https://madeonsol.com/api/v1/rhc/tokens/locks`
- Auth: HTTP bearer

Newest token lock / vesting contracts CREATED on Robinhood Chain, newest first: who just locked tokens, of what, how much, until when. Decoded from the locker contracts' own events on our node (services/rhc-lock-tracker, chain-wide topic0 probe with no address filter — a new deployment of a known locker shape is caught on first sight): PinkLock-compatible, HoodLock (+ vesting), Team Finance-compatible (+ LP NFT), Titan Locker (token / LP / position), UNCX v2/v3-compatible LP lockers, Sablier Lockup v4 (linear / tranched / dynamic). Every row carries the on-chain schedule (start / cliff / end, cliff amount, tranches) and a live derived view: `locked_*` (still locked right now), `unlocked_*`, `next_unlock` (cliff | final | tranche), `status`; `sender` is the depositor/creator (compare with the token's deployer for a dev lock), `recipient` the beneficiary when different; `cancelable` / `cancelable_by_sender` from Sablier's flag (null where the family does not say). Amounts are RAW base units as strings; ui/usd/pct are null when decimals/price are unknown. **Create-only tape**: withdrawals / cancels are NOT tracked (no RHC locker publishes a verified release event shape) — `withdrawn` is null (unknown), never 0, and the `coverage` block says `withdrawals_tracked: false`. LP locks (subject=lp — RHC launchpads auto-lock LP on every launch) are stored but excluded unless `subject=lp|all`, carry pair/liquidity units and never claim usd/pct. Cursor: `cursor=` (`pagination.next_cursor`, strict (created_at, id) — preferred), `since=` (`pagination.next_since`) / legacy `before=`. The status / min_usd / min_pct_of_supply post-filters are served by a bounded scan (`pagination.scan`). Pushed live as `rhc:token_lock` on WS channel `rhc:token_locks`. Tier: **PRO+**.

## Query parameters

- `limit` integer
- `since` string, date-time
- `before` string, date-time
- `cursor` string
- `token` string
- `sender` string
- `recipient` string
- `locker` string
- `family` 'pinklock' | 'teamfinance' | 'teamfinance-nft' | 'uncx-v2-lp' | 'uncx-v3-lp' | 'uncx-vesting' | 'vesting-fork' | 'goplus' | 'titan' | 'titan-position' | 'titan-vesting' | 'hoodlock' | 'hoodlock-vesting' | 'sablier' | 'stonkbrokers-vesting'
- `kind` 'lock' | 'vesting'
- `subject` 'token' | 'lp' | 'all'
- `status` 'active' | 'completed'
- `min_usd` number
- `min_pct_of_supply` number

## Response `200`

OK.

- object
  - `chain` 'robinhood'
  - `locks` object[]
    - `lock_id` string: `<tx_hash>:<log_index>` — the row identity.
    - `locker` string
    - `locker_name` string, nullable
    - `provider` object: Who runs the lock contract, and how sure we are. identity: verified = a known provider deployment (by contract address); compatible = only the event shape (ABI) matches a known provider, the operator is NOT identified; unverified = unknown. id/website_url are null unless verified. lock_url is a per-lock page on the provider's site, emitted ONLY where the URL format is proven (HoodLock vault: /proof/lock/{locker_lock_id}); otherwise null (never guessed).
      - `id` string, nullable
      - `name` string, nullable
      - `identity` 'verified' | 'compatible' | 'unverified'
      - `compatible_with` string, nullable
      - `website_url` string, nullable
      - `lock_url` string, nullable
    - `explorer` object: Independent evidence on Blockscout.
      - `locker_url` string, nullable
      - `creation_tx_url` string, nullable
    - `price_usd` number, nullable: Token price used for the *_usd fields (null when unknown or guarded as phantom).
    - `seconds_until_end` integer, nullable: Seconds until fully unlocked (>= 0). 0 once completed; null when there is no end date (perpetual) or the lock was cancelled/closed.
    - `seconds_until_next_unlock` integer, nullable: Seconds until next_unlock.at (>= 0); null when there is no next unlock.
    - `family` string
    - `family_name` string
    - `locker_lock_id` string, nullable
    - `kind` 'lock' | 'vesting'
    - `subject` 'token' | 'lp'
    - `status` 'active' | 'completed'
    - `token_address` string
    - `lp` object, nullable: Only on subject=lp: kind (v2_pair | v3_position | v4_position), pool, token0, token1, token_id.
    - `sender` string: Depositor / creator — the dev-lock comparison key.
    - `recipient` string, nullable
    - `tx_sender` string, nullable
    - `name` string, nullable
    - `amount_raw` string, nullable
    - `amount` number, nullable
    - `amount_usd` number, nullable
    - `amount_pct_of_supply` number, nullable
    - `amount_unit` string, nullable: token | lp_token | liquidity (uncx-v3) — LP rows never claim usd/pct.
    - `locked_raw` string, nullable
    - `locked` number, nullable
    - `locked_usd` number, nullable
    - `locked_pct_of_supply` number, nullable
    - `unlocked_raw` string, nullable
    - `unlocked` number, nullable
    - `withdrawn_raw` string, nullable: Always null — withdrawals are not tracked on RHC.
    - `withdrawn` number, nullable
    - `start_at` string, nullable
    - `cliff_at` string, nullable
    - `end_at` string, nullable
    - `cliff_amount_raw` string, nullable
    - `cliff_amount` number, nullable
    - `continuous` boolean: Linear per-second release between cliff and end.
    - `perpetual` boolean
    - `schedule` object[], nullable: Tranches (Sablier tranched/dynamic): release_at, amount_raw, amount.
    - `next_unlock` object, nullable: at, kind (cliff | final | tranche), amount_raw, amount, amount_usd.
    - `cancelable` boolean, nullable
    - `cancelable_by_sender` boolean, nullable
    - `transferable` boolean, nullable
    - `created_at` string, date-time: = block_time of the creation event.
    - `created_at_estimated` boolean: Always false on RHC.
    - `block_number` integer
    - `block_time` string, date-time
    - `tx_hash` string
    - `log_index` integer
    - `layout_verified` boolean: false only for families decoded from published source without a live log yet (titan-vesting).
    - `token` object
      - `symbol` string, nullable
      - `name` string, nullable
      - `decimals` integer, nullable
      - `price_usd` number, nullable
      - `market_cap_usd` number, nullable
      - `liquidity_usd` number, nullable
  - `pagination` object
    - `limit` integer
    - `count` integer
    - `has_more` boolean: false only when the candidate feed is exhausted (not merely when the page is short).
    - `next_cursor` string, nullable: Opaque keyset cursor for the next (older) page — pass as `cursor=`. null = end of feed. PREFERRED over the strict-timestamp legacy cursor, which skips same-timestamp siblings of the boundary row.
    - `next_since` string, nullable
    - `next_before` string, nullable: Legacy: created_at of the last row consumed (strict bound — skips ties).
    - `scan` object: Present when a filter had to be applied after the candidate fetch (audit F09): has_more is then false only when the candidates ran out, and scan_truncated=true means the per-request scan budget ran out first — more matches MAY exist past next_cursor.
      - `post_filtered` boolean
      - `scanned` integer: Candidate rows examined
      - `scan_truncated` boolean: The per-request scan budget ran out before `limit` matches — more matches MAY exist past the cursor/offset
      - `scan_budget` integer
  - `stream` object: WS pointer: channel rhc:token_locks, event rhc:token_lock.
  - `coverage` object: Honesty block: families, withdrawals_tracked false, cancels_tracked false, lp_locks, note.
  - `meta` object

## Other responses

- `400`: Invalid query parameters (code invalid_query).
- `401`: Missing or invalid API key, or an expired free-tier key. Authentication-stage error: no `X-RateLimit-*` headers, no `_rid`.
- `403`: PRO+ required.
- `429`: Too many requests. Every 429 carries `Retry-After`. A quota 429 (`rate_limit_exceeded`, with `X-RateLimit-*`), a heavy-query concurrency 429 (`error_kind` `concurrency`/`capacity`), or the per-IP authentication-failure block (30 failed auths/min; plain `error` + `retry_after_seconds`, no `X-RateLimit-*`).
- `500`: Unexpected server error from the route (`Internal server error`, with `_rid`). Safe to retry with backoff. A failed API-key lookup is a 503 `auth_backend_unavailable`, never a 500.
- `503`: Temporarily unavailable — a Postgres statement timeout while the dataset is under load or maintenance (`statement_timeout`, with `X-RateLimit-*` and `_rid`), or the API-key lookup itself unavailable (`auth_backend_unavailable`, authentication stage: no `X-RateLimit-*`, no `_rid`, not counted as a failed authentication). Always carries `Retry-After`; honour it.

## Changes

- **2026-10-19** `9ea9a35ee2bf`: 1 info
  - added the new enum value `stonkbrokers-vesting` to the `query` request parameter `family`

[Operation history](https://skmtc.dev/madeonsol/apis/madeonsol-robinhood-chain-api/changes/rhc/tokens/locks/get.md)

---

[API](https://skmtc.dev/madeonsol/apis/madeonsol-robinhood-chain-api.md) · [All operations](https://skmtc.dev/madeonsol/apis/madeonsol-robinhood-chain-api/llms.txt) · [OpenAPI document](https://skmtc.dev/madeonsol/apis/madeonsol-robinhood-chain-api/revisions/9ea9a35ee2bf?raw)
