IPOVest API Reference
A fast, read-only JSON API for IPOs, grey-market premium (GMP), offer-for-sale (OFS) and pre-IPO data in the Indian market. Everything you need to integrate, in one place.
Introduction
The IPOVest API gives you structured access to current and past IPOs, live GMP, OFS issues and unlisted-share prices. All endpoints are GET, return application/json, and are served over HTTPS from Cloudflare's global edge network.
https://api.ipovest.com/v1X-API-KEYapplication/jsonGET (read-only)- No SDK required — any HTTP client works.
- No webhooks; poll at the cadence described in Data freshness.
- CORS is open (
Access-Control-Allow-Origin: *), but never expose your key in browser code.
Quickstart
1. Create a free account to get a key. 2. Send it in the X-API-KEY header. 3. Parse the JSON.
curl -H "X-API-KEY: $IPOVEST_KEY" \ "https://api.ipovest.com/v1/gmp?status=Open"
import os, requests
r = requests.get(
"https://api.ipovest.com/v1/gmp",
headers={"X-API-KEY": os.environ["IPOVEST_KEY"]},
params={"status":"Open"},
timeout=10,
)
r.raise_for_status()
print(r.json())const res = await fetch("https://api.ipovest.com/v1/gmp?status=Open", {
headers: { "X-API-KEY": process.env.IPOVEST_KEY },
});
if (!res.ok) throw new Error(`IPOVest API ${res.status}`);
console.log(await res.json());$ch = curl_init("https://api.ipovest.com/v1/gmp?status=Open");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-KEY: " . getenv("IPOVEST_KEY")],
CURLOPT_TIMEOUT => 10,
]);
$data = json_decode(curl_exec($ch), true);Check what your key can do at any time with GET /me.
Authentication
Authenticate every request with your personal API key. Keys look like ipv_ followed by 32 characters.
X-API-KEY: ipv_your_api_key
Authorization: Bearer ipv_your_api_key
Keep it secret. Treat the key like a password: store it in an environment variable on your server, keep it out of version control, and never ship it in a website or mobile app. If it leaks, press Regenerate key in the dashboard — the old key stops working immediately. Keys are stored hashed, so we can show a key only once.
Query-string keys (?api_key=) are not supported, to keep keys out of server and proxy logs.
Plans & access
Each endpoint requires a minimum plan. Calling an endpoint your plan does not include returns 402 plan_required with the plan you need.
| Endpoint | Free | Basic | Pro |
|---|---|---|---|
GET /me | ✓ | ✓ | ✓ |
GET /ipos | ✓ | ✓ | ✓ |
GET /gmp | ✓ | ✓ | ✓ |
GET /ipos/{slug} | — | ✓ | ✓ |
GET /ofs · /ofs/{slug} | — | ✓ | ✓ |
GET /unlisted · /unlisted/{slug} | — | — | ✓ |
GET /ipos/{slug}/gmp/history | — | — | ✓ |
When a paid plan expires, the key keeps working on the Free plan. See pricing to upgrade.
Rate limits
Limits apply per API key, on a per-minute and a per-day basis (day = UTC calendar day).
| Plan | Per minute | Per day | Price |
|---|---|---|---|
| Free | 10 | 100 | Free |
| Basic | 30 | 2,000 | ₹499 / month |
| Pro | 60 | 10,000 | ₹1,499 / month |
Rate-limit headers
| Field | Type | Description |
|---|---|---|
X-RateLimit-Limit-Day | integer | Your daily allowance. |
X-RateLimit-Remaining-Day | integer | Requests left today (never below 0). |
X-RateLimit-Limit-Minute | integer | Your per-minute allowance. |
Retry-After | seconds | Sent with a per-minute 429; wait this long before retrying. |
Every authenticated request to a valid endpoint counts toward your limits — including 402 and 404 (unknown slug) responses. Requests to unknown paths and requests with a missing or invalid key are not counted.
Response format
List endpoints wrap results in data together with paging fields. Detail endpoints return a single object in data.
{
"data": [ { /* IPO object */ } ],
"limit": 100,
"offset": 0
}Conventions
- Dates are ISO 8601 —
YYYY-MM-DDfor calendar dates and full UTC timestamps (2026-11-04T09:31:15.000000Z) for moments in time. - Numbers that represent money or ratios (e.g.
issue_price,subscription_total,gmp.price) may be returned as strings. Always parse them (parseFloat/float()) before arithmetic. - Missing values are
null, never an empty string. Don't assume any field is present — e.g. an upcoming IPO has nolisting_price. - Currency is Indian rupees (₹) unless stated;
issue_sizeis in ₹ crore. - Treat unknown extra fields as additive, non-breaking changes.
Pagination & filters
List endpoints (/ipos, /gmp, /ofs, /unlisted) accept the following query parameters:
| Parameter | Type | Description |
|---|---|---|
status | string | Exact, case-sensitive match on the record's status — e.g. Open or Closed for IPOs, open/closed (lowercase) for OFS. |
limit | integer | Page size. Default 100, min 1, max 500. |
offset | integer | Number of records to skip. Default 0. |
Results are ordered by most recently updated first. To page through everything, increase offset by limit until a response returns fewer than limit rows.
curl -H "X-API-KEY: $IPOVEST_KEY" \ "https://api.ipovest.com/v1/ipos?limit=50&offset=50"
offset, limit, rows = 0, 100, []
while True:
page = requests.get(f"https://api.ipovest.com/v1/ipos",
headers=H, params={"limit": limit, "offset": offset}).json()["data"]
rows += page
if len(page) < limit:
break
offset += limitEndpoints
List IPOs
Returns every IPO we track — upcoming, open, closed and listed — Mainboard and SME.
Query parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by Open, Closed or Upcoming. |
limit | integer | 1–500, default 100. |
offset | integer | Default 0. |
Request
curl -H "X-API-KEY: $IPOVEST_KEY" \ "https://api.ipovest.com/v1/ipos?status=Open&limit=10"
import os, requests
r = requests.get(
"https://api.ipovest.com/v1/ipos",
headers={"X-API-KEY": os.environ["IPOVEST_KEY"]},
params={"status":"Open","limit":"10"},
timeout=10,
)
r.raise_for_status()
print(r.json())const res = await fetch("https://api.ipovest.com/v1/ipos?status=Open&limit=10", {
headers: { "X-API-KEY": process.env.IPOVEST_KEY },
});
if (!res.ok) throw new Error(`IPOVest API ${res.status}`);
console.log(await res.json());$ch = curl_init("https://api.ipovest.com/v1/ipos?status=Open&limit=10");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-KEY: " . getenv("IPOVEST_KEY")],
CURLOPT_TIMEOUT => 10,
]);
$data = json_decode(curl_exec($ch), true);Response
{
"data": [
{
"slug": "manipal-payment-identity-solutions-ipo",
"name": "Manipal Payment & Identity Solutions",
"display_name": "Manipal Payment & Identity Solutions",
"type": "Mainboard",
"sub_type": "Equity",
"status": "Closed",
"stage": "live",
"dates_tba": false,
"is_listed": true,
"logo": "https://api.ipovest.com/img/logos/manipal-payment-identity-solutions-ipo.webp",
"open_date": "2026-09-09",
"close_date": "2026-09-11",
"allotment_date": "2026-09-15",
"listing_date": "2026-09-17",
"price_band": "322-339",
"price_min": 322,
"price_max": 339,
"issue_price": "339",
"issue_size": "805",
"lot_size": 44,
"min_investment": 14916,
"gmp": {
"price": "-6",
"percentage": "-1.77",
"kostak": null,
"subject_to_sauda": null,
"estimated_listing_price": 333,
"updated_at": "2026-09-17T02:54:05.000000Z",
"updated_at_label": "17 Sep 2026, 08:24 AM IST"
},
"subscription_total": "1.42",
"listing_price": "330",
"listing_gain_percent": -2.65,
"cmp": "331.35",
"current_gain_percent": -2.26
}
],
"limit": 10,
"offset": 0
}IPO object
| Field | Type | Description |
|---|---|---|
slug | string | Unique, URL-safe identifier. Use it in /ipos/{slug} and the GMP history route. |
name | string | Company name. |
display_name | string | Name as it should be displayed. |
type | string | Mainboard or SME. |
sub_type | string | Exchange or category, e.g. Equity, BSE SME, NSE SME. |
status | string | Open, Closed or Upcoming. Capitalised exactly like this. |
stage | string | Lifecycle stage, e.g. live. |
dates_tba | boolean | true if the dates are not announced yet. |
is_listed | boolean | true once the shares have listed. |
logo | string | null | Absolute URL of the company logo. |
open_date | string | null | Issue opening date, YYYY-MM-DD. |
close_date | string | null | Issue closing date. |
allotment_date | string | null | Allotment finalisation date. |
listing_date | string | null | Expected or actual listing date. |
price_band | string | null | Price band text, e.g. "322-339". |
price_min | number | null | Lower end of the band (₹). |
price_max | number | null | Upper end of the band (₹). |
issue_price | string | null | Final issue price (₹). |
issue_size | string | null | Issue size in ₹ crore. |
lot_size | integer | null | Shares per lot. |
min_investment | integer | null | Minimum retail investment per lot (₹). |
gmp | object | null | Latest grey-market premium — see GMP object. |
subscription_total | string | null | Total subscription, in times (e.g. "1.42"). |
listing_price | string | null | Price at listing (₹), once listed. |
listing_gain_percent | number | null | Gain/loss at listing, in percent. |
cmp | string | null | Current market price (₹) for listed IPOs. |
current_gain_percent | number | null | Gain/loss versus issue price, in percent. |
Get an IPO
Returns the full record of a single IPO, identified by its slug.
Path parameters
| Parameter | Type | Description |
|---|---|---|
slugrequired | string | The IPO's slug from the list endpoint. |
Request
curl -H "X-API-KEY: $IPOVEST_KEY" \ "https://api.ipovest.com/v1/ipos/manipal-payment-identity-solutions-ipo"
import os, requests
r = requests.get(
"https://api.ipovest.com/v1/ipos/manipal-payment-identity-solutions-ipo",
headers={"X-API-KEY": os.environ["IPOVEST_KEY"]},
timeout=10,
)
r.raise_for_status()
print(r.json())const res = await fetch("https://api.ipovest.com/v1/ipos/manipal-payment-identity-solutions-ipo", {
headers: { "X-API-KEY": process.env.IPOVEST_KEY },
});
if (!res.ok) throw new Error(`IPOVest API ${res.status}`);
console.log(await res.json());$ch = curl_init("https://api.ipovest.com/v1/ipos/manipal-payment-identity-solutions-ipo");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-KEY: " . getenv("IPOVEST_KEY")],
CURLOPT_TIMEOUT => 10,
]);
$data = json_decode(curl_exec($ch), true);Response
{
"data": { /* IPO object — same fields as in the list */ }
}Returns 404 not_found if the slug does not exist. On the Free plan you receive 402 plan_required (upgrade_to: "basic").
Current GMP
A lightweight list of every IPO with its latest grey-market premium — ideal for dashboards and bots.
Query parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by Open, Closed or Upcoming. |
limit | integer | 1–500, default 100. |
offset | integer | Default 0. |
Request
curl -H "X-API-KEY: $IPOVEST_KEY" \ "https://api.ipovest.com/v1/gmp?status=Open"
import os, requests
r = requests.get(
"https://api.ipovest.com/v1/gmp",
headers={"X-API-KEY": os.environ["IPOVEST_KEY"]},
params={"status":"Open"},
timeout=10,
)
r.raise_for_status()
print(r.json())const res = await fetch("https://api.ipovest.com/v1/gmp?status=Open", {
headers: { "X-API-KEY": process.env.IPOVEST_KEY },
});
if (!res.ok) throw new Error(`IPOVest API ${res.status}`);
console.log(await res.json());$ch = curl_init("https://api.ipovest.com/v1/gmp?status=Open");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-KEY: " . getenv("IPOVEST_KEY")],
CURLOPT_TIMEOUT => 10,
]);
$data = json_decode(curl_exec($ch), true);Response
{
"data": [
{
"slug": "sfc-environmental-technologies-ipo",
"name": "SFC Environmental Technologies",
"type": "Mainboard",
"status": "Closed",
"stage": "live",
"dates_tba": false,
"price_band": "313-330",
"issue_price": "330",
"gmp": {
"price": "15.5",
"percentage": "4.70",
"kostak": null,
"subject_to_sauda": "500,,7000",
"estimated_listing_price": 345.5,
"updated_at": "2026-11-03T02:54:03.000000Z",
"updated_at_label": "03 Nov 2026, 08:24 AM IST"
}
}
],
"limit": 100,
"offset": 0
}GMP object
| Field | Type | Description |
|---|---|---|
price | string | null | GMP in ₹ per share. Negative means trading below issue price. |
percentage | string | null | GMP as a percentage of the issue price. |
kostak | string | null | Kostak rate (₹ per application), when quoted. |
subject_to_sauda | string | null | Subject-to-sauda rate, when quoted. |
estimated_listing_price | number | null | Issue price + GMP — an estimate, not a guarantee. |
updated_at | string | When the GMP was last updated (UTC ISO 8601). |
updated_at_label | string | Human-readable update time in IST. |
GMP is an unofficial market indicator. It can change quickly and may differ from actual listing prices. It is not investment advice.
GMP history
Time series of GMP snapshots we recorded for an IPO, oldest first. A snapshot is stored on each 30-minute refresh.
Path parameters
| Parameter | Type | Description |
|---|---|---|
slugrequired | string | The IPO's slug. |
Request
curl -H "X-API-KEY: $IPOVEST_KEY" \ "https://api.ipovest.com/v1/ipos/vedant-poly-fabs-sme-ipo/gmp/history"
import os, requests
r = requests.get(
"https://api.ipovest.com/v1/ipos/vedant-poly-fabs-sme-ipo/gmp/history",
headers={"X-API-KEY": os.environ["IPOVEST_KEY"]},
timeout=10,
)
r.raise_for_status()
print(r.json())const res = await fetch("https://api.ipovest.com/v1/ipos/vedant-poly-fabs-sme-ipo/gmp/history", {
headers: { "X-API-KEY": process.env.IPOVEST_KEY },
});
if (!res.ok) throw new Error(`IPOVest API ${res.status}`);
console.log(await res.json());$ch = curl_init("https://api.ipovest.com/v1/ipos/vedant-poly-fabs-sme-ipo/gmp/history");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-KEY: " . getenv("IPOVEST_KEY")],
CURLOPT_TIMEOUT => 10,
]);
$data = json_decode(curl_exec($ch), true);Response
{
"data": [
{ "captured_at": "2026-11-04T12:12:20.114Z", "gmp_price": 1 },
{ "captured_at": "2026-11-04T12:42:23.668Z", "gmp_price": 1 },
{ "captured_at": "2026-11-04T13:12:21.902Z", "gmp_price": 2 }
]
}| Field | Type | Description |
|---|---|---|
captured_at | string | When IPOVest recorded the snapshot (UTC ISO 8601). |
gmp_price | number | null | GMP in ₹ at that moment. |
History starts from when IPOVest began recording. IPOs not yet seen return an empty data array. This endpoint is not paginated.
List OFS issues
Offer-for-sale (OFS) issues on the stock exchanges, split by category.
Query parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Lowercase, e.g. open or closed. |
limit | integer | 1–500, default 100. |
offset | integer | Default 0. |
Request
curl -H "X-API-KEY: $IPOVEST_KEY" \ "https://api.ipovest.com/v1/ofs?status=closed"
import os, requests
r = requests.get(
"https://api.ipovest.com/v1/ofs",
headers={"X-API-KEY": os.environ["IPOVEST_KEY"]},
params={"status":"closed"},
timeout=10,
)
r.raise_for_status()
print(r.json())const res = await fetch("https://api.ipovest.com/v1/ofs?status=closed", {
headers: { "X-API-KEY": process.env.IPOVEST_KEY },
});
if (!res.ok) throw new Error(`IPOVest API ${res.status}`);
console.log(await res.json());$ch = curl_init("https://api.ipovest.com/v1/ofs?status=closed");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-KEY: " . getenv("IPOVEST_KEY")],
CURLOPT_TIMEOUT => 10,
]);
$data = json_decode(curl_exec($ch), true);Response
{
"data": [
{
"slug": "sri-lotus-developers-non-retail-nov-2026",
"company": "Sri Lotus Developers and Realty",
"short_name": "Sri Lotus Developers",
"category": "Non-Retail",
"status": "closed",
"open_date": "2026-11-02T18:30:00.000000Z",
"close_date": "2026-11-02T18:30:00.000000Z",
"floor_price": 240,
"discount": "0.00",
"nse_symbol": "LOTUSDEV",
"bse_scripcode": "544469",
"subscription": 1.16
}
],
"limit": 100,
"offset": 0
}OFS object
| Field | Type | Description |
|---|---|---|
slug | string | Unique identifier. |
company | string | Company name. |
short_name | string | Short display name. |
category | string | Investor category, e.g. Non-Retail or Retail. |
status | string | open, closed or upcoming (lowercase). |
open_date | string | Opening time (UTC). |
close_date | string | Closing time (UTC). |
floor_price | number | Floor price in ₹. |
discount | string | Discount to the previous close, in percent. |
nse_symbol | string | null | NSE trading symbol. |
bse_scripcode | string | null | BSE scrip code. |
subscription | number | null | Subscription in times. |
Get an OFS issue
Returns a single OFS record by slug.
Path parameters
| Parameter | Type | Description |
|---|---|---|
slugrequired | string | The OFS slug. |
Request
curl -H "X-API-KEY: $IPOVEST_KEY" \ "https://api.ipovest.com/v1/ofs/sri-lotus-developers-non-retail-nov-2026"
import os, requests
r = requests.get(
"https://api.ipovest.com/v1/ofs/sri-lotus-developers-non-retail-nov-2026",
headers={"X-API-KEY": os.environ["IPOVEST_KEY"]},
timeout=10,
)
r.raise_for_status()
print(r.json())const res = await fetch("https://api.ipovest.com/v1/ofs/sri-lotus-developers-non-retail-nov-2026", {
headers: { "X-API-KEY": process.env.IPOVEST_KEY },
});
if (!res.ok) throw new Error(`IPOVest API ${res.status}`);
console.log(await res.json());$ch = curl_init("https://api.ipovest.com/v1/ofs/sri-lotus-developers-non-retail-nov-2026");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-KEY: " . getenv("IPOVEST_KEY")],
CURLOPT_TIMEOUT => 10,
]);
$data = json_decode(curl_exec($ch), true);Response
{
"data": { /* OFS object */ }
}List pre-IPO / unlisted shares
Indicative prices for unlisted shares traded in the pre-IPO market, with dealer quotes and implied market cap.
Query parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter on the record's status when present. |
limit | integer | 1–500, default 100. |
offset | integer | Default 0. |
Request
curl -H "X-API-KEY: $IPOVEST_KEY" \ "https://api.ipovest.com/v1/unlisted?limit=20"
import os, requests
r = requests.get(
"https://api.ipovest.com/v1/unlisted",
headers={"X-API-KEY": os.environ["IPOVEST_KEY"]},
params={"limit":"20"},
timeout=10,
)
r.raise_for_status()
print(r.json())const res = await fetch("https://api.ipovest.com/v1/unlisted?limit=20", {
headers: { "X-API-KEY": process.env.IPOVEST_KEY },
});
if (!res.ok) throw new Error(`IPOVest API ${res.status}`);
console.log(await res.json());$ch = curl_init("https://api.ipovest.com/v1/unlisted?limit=20");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-KEY: " . getenv("IPOVEST_KEY")],
CURLOPT_TIMEOUT => 10,
]);
$data = json_decode(curl_exec($ch), true);Response
{
"data": [
{
"slug": "prabhudas-lilladher-advisory-services",
"name": "Prabhudas Lilladher Advisory Services",
"display_name": "Prabhudas Lilladher Advisory Services",
"sector": "Financial Services",
"isin": "INE894K01025",
"logo": "https://api.ipovest.com/img/unlisted_logos/prabhudas-lilladher-advisory-services.webp",
"ltp": 365,
"ltp_updated_at": "2026-11-04T09:31:15.000000Z",
"price_date": "2026-11-04",
"price_source": "unlistedzone",
"price_type": "indicative_dealer_quote",
"is_stale": false,
"week_52_high": 395,
"week_52_low": 365,
"lot_size": 500,
"min_investment": 182500,
"implied_market_cap_cr": 2289.07,
"dealer_range": { "low": 365, "high": 375, "median": 370, "dealers": 2, "date": "2026-11-04" },
"ipo_status": null,
"ipo_journey_status": null
}
],
"limit": 20,
"offset": 0
}Unlisted object
| Field | Type | Description |
|---|---|---|
slug | string | Unique identifier. |
name / display_name | string | Company name. |
sector | string | null | Industry sector. |
isin | string | null | ISIN code. |
logo | string | null | Logo URL. |
ltp | number | null | Last traded / indicative price (₹). |
ltp_updated_at | string | null | When ltp was updated (UTC). |
price_date | string | null | Date of the price, YYYY-MM-DD. |
price_source | string | null | Where the price came from. |
price_type | string | null | e.g. indicative_dealer_quote. Prices are indicative, not exchange prices. |
is_stale | boolean | true if the price has not been refreshed recently. |
week_52_high / week_52_low | number | null | 52-week range (₹). |
lot_size | integer | null | Typical lot size. |
min_investment | number | null | Lot size × price (₹). |
implied_market_cap_cr | number | null | Implied market capitalisation in ₹ crore. |
dealer_range | object | null | low, high, median, number of dealers and quote date. |
ipo_status / ipo_journey_status | string | null | Progress towards an IPO, when announced. |
Unlisted-share prices come from dealer quotes, are illiquid and can be stale. Always check is_stale and price_type.
Get an unlisted company
Returns a single unlisted-share record by slug.
Path parameters
| Parameter | Type | Description |
|---|---|---|
slugrequired | string | The unlisted slug. |
Request
curl -H "X-API-KEY: $IPOVEST_KEY" \ "https://api.ipovest.com/v1/unlisted/prabhudas-lilladher-advisory-services"
import os, requests
r = requests.get(
"https://api.ipovest.com/v1/unlisted/prabhudas-lilladher-advisory-services",
headers={"X-API-KEY": os.environ["IPOVEST_KEY"]},
timeout=10,
)
r.raise_for_status()
print(r.json())const res = await fetch("https://api.ipovest.com/v1/unlisted/prabhudas-lilladher-advisory-services", {
headers: { "X-API-KEY": process.env.IPOVEST_KEY },
});
if (!res.ok) throw new Error(`IPOVest API ${res.status}`);
console.log(await res.json());$ch = curl_init("https://api.ipovest.com/v1/unlisted/prabhudas-lilladher-advisory-services");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-KEY: " . getenv("IPOVEST_KEY")],
CURLOPT_TIMEOUT => 10,
]);
$data = json_decode(curl_exec($ch), true);Response
{
"data": { /* Unlisted object */ }
}Account & usage
Returns your effective plan, limits and today's usage. Handy for health checks and quota monitoring.
Request
curl -H "X-API-KEY: $IPOVEST_KEY" \ "https://api.ipovest.com/v1/me"
import os, requests
r = requests.get(
"https://api.ipovest.com/v1/me",
headers={"X-API-KEY": os.environ["IPOVEST_KEY"]},
timeout=10,
)
r.raise_for_status()
print(r.json())const res = await fetch("https://api.ipovest.com/v1/me", {
headers: { "X-API-KEY": process.env.IPOVEST_KEY },
});
if (!res.ok) throw new Error(`IPOVest API ${res.status}`);
console.log(await res.json());$ch = curl_init("https://api.ipovest.com/v1/me");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-KEY: " . getenv("IPOVEST_KEY")],
CURLOPT_TIMEOUT => 10,
]);
$data = json_decode(curl_exec($ch), true);Response
{
"plan": "pro",
"plan_label": "Pro",
"plan_expires": "2026-12-04T23:59:59.000Z",
"limits": { "per_minute": 60, "per_day": 10000 },
"used_today": 128
}| Field | Type | Description |
|---|---|---|
plan | string | free, basic or pro — your effective plan (expired paid plans show as free). |
plan_label | string | Display name of the plan. |
plan_expires | string | null | Expiry timestamp for paid plans, otherwise null. |
limits | object | per_minute and per_day. |
used_today | integer | Requests counted so far today (UTC), including this one. |
Object reference
Field tables are shown next to each endpoint: IPO, GMP, OFS, Unlisted. Types use | null when the value may be absent. Future versions may add fields; build parsers that ignore unknown keys.
Errors
Errors use standard HTTP status codes and a JSON body with a machine-readable error code and, usually, a human-readable message.
{
"error": "plan_required",
"message": "This endpoint needs the Basic plan.",
"upgrade_to": "basic",
"upgrade_url": "https://api.ipovest.com/plans"
}| Status | error | Meaning & what to do |
|---|---|---|
| 401 | missing_api_key | No key was sent. Add the X-API-KEY header. |
| 401 | invalid_api_key | The key is wrong or has been regenerated. Copy the current key from the dashboard. |
| 402 | plan_required | Your plan doesn't include this endpoint. upgrade_to names the cheapest plan that does. |
| 404 | not_found | Unknown endpoint, or no record with that slug. |
| 405 | method_not_allowed | Only GET (and CORS OPTIONS) are supported. |
| 429 | rate_limited | Per-minute limit hit. Wait for Retry-After seconds. |
| 429 | daily_quota_exceeded | Daily quota used up; it resets at 00:00 UTC. Upgrade for a higher quota. |
| 500 | server_error | Unexpected problem on our side. Retry with backoff; contact us if it persists. |
Best practices
- Cache. Data refreshes every 30 minutes, so polling more often wastes quota. Cache responses for at least 5–15 minutes on your server.
- Fetch once, serve many. Call the API from your backend on a schedule and serve your own users from your cache or database.
- Back off on errors. Retry
429/5xxwith exponential backoff and jitter; honourRetry-After. Never retry401/402/404in a loop. - Set timeouts (5–10 s) on every request.
- Monitor quota through the
X-RateLimit-Remaining-Dayheader orGET /me. - Parse defensively. Convert numeric strings, treat every field as nullable, and ignore unknown fields.
- Protect your key. Server-side only, in environment variables; rotate it if exposed.
import time, random, requests
def get(path, **params):
for attempt in range(5):
r = requests.get(f"https://api.ipovest.com/v1{path}", params=params, timeout=10,
headers={"X-API-KEY": KEY})
if r.status_code in (429, 500, 502, 503, 504):
wait = int(r.headers.get("Retry-After", 2 ** attempt))
time.sleep(wait + random.random())
continue
r.raise_for_status()
return r.json()
raise RuntimeError("IPOVest API unavailable")async function get(path, params = {}) {
const url = new URL("https://api.ipovest.com/v1" + path);
Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
for (let i = 0; i < 5; i++) {
const res = await fetch(url, { headers: { "X-API-KEY": process.env.IPOVEST_KEY } });
if ([429, 500, 502, 503, 504].includes(res.status)) {
const wait = Number(res.headers.get("retry-after")) || 2 ** i;
await new Promise(r => setTimeout(r, wait * 1000 + Math.random() * 500));
continue;
}
if (!res.ok) throw new Error("IPOVest API " + res.status);
return res.json();
}
throw new Error("IPOVest API unavailable");
}Data freshness
IPOVest refreshes its dataset every 30 minutes. GMP snapshots are recorded on the same cycle. Fields such as gmp.updated_at and ltp_updated_at tell you when the underlying value last changed at the source, which can be earlier than our last refresh.
New IPOs and status changes (e.g. Upcoming → Open → Closed) appear after the next sync. There is no real-time streaming.
Usage & terms
- Data is provided for information only and may contain errors or delays. It is not investment advice; GMP and unlisted prices are unofficial indicators.
- Keep your key private and use it only for your own applications. Don't resell or redistribute the raw dataset as a competing API, and don't attempt to bypass rate limits.
- We may pause keys that abuse the service.
FAQ
Why do I get 402 on an endpoint?
Your plan doesn't include it. Check the plan matrix and upgrade from the pricing page.
Why are some numbers strings?
Values like prices and percentages are passed through from the source with their original precision. Parse them before doing maths.
How do I get the list of Open IPOs only?
Use GET /ipos?status=Open (note the capital “O”).
What happens when my plan expires?
The key keeps working on the Free plan limits and endpoints. Renew any time; renewing early extends your current period.
I regenerated my key — can I recover the old one?
No. Keys are stored hashed and are shown once. Generate a new key and update your apps.
Is there a sandbox or test key?
The Free plan is the sandbox — it's free forever, with a small daily quota.
Changelog
| v1 | Nov 2026 | Initial release: IPOs, GMP (current + history), OFS, unlisted shares, account endpoint. |
Breaking changes will ship under a new version path (/v2); /v1 will keep working.