# Vigil API - India Red Flag Tracker

Corporate red-flag signals for ~750 Indian listed companies, sourced from NSE (National Stock Exchange of India) filings. Seven tables covering the corporate-governance surface: credit_ratings (instrument-level from CRISIL/ICRA/CARE/India Ratings/Acuite/Brickwork/Infomerics, ~10K records Jan 2024 onward), insider_trading (SEBI PIT promoter/director/KMP buy-sell-pledge, ~31K records Feb 2024 onward), pledge_data (promoter pledge snapshot ~1,500 companies, latest quarter), sast_disclosures (SAST Reg 29 substantial acquisitions/takeover code, ~10K records Jan 2024 onward), encumbrance_events (SAST Reg 31/32 pledge creation/release/invocation, ~1,400 events), surveillance_flags (ASM/GSM/ESM/IRP/ICA regulatory flags, ~400 flagged), rpt_transactions (Related Party Transactions from XBRL, ~250K rows for Nifty Total Market). Updated daily. No auth required.

**How the data is built:** ingested from NSE's public filings (XBRL where available, structured downloads elsewhere), normalized into per-table schemas with `broadcast_datetime` as the freshness anchor. Historical records stay queryable; source-filed values are published as filed.

**Quick start:**
- `GET /vigil/v1/tables` - all 7 tables with row counts, columns, and last-sync
- `GET /vigil/v1/companies` - master list of covered companies with symbol + ISIN
- `GET /vigil/v1/company/RELIANCE` - all filings across all tables for one company
- `GET /vigil/v1/data/insider_trading?limit=100` - paginated table data
- `GET /vigil/v1/freshness` - last sync time per table
- Bulk: `GET /vigil/v1/download/all` (~20 MB zip of all tables in one file) or per-table `GET /vigil/v1/download/{table_name}`.

**Gotchas:** dates are YYYY-MM-DD or ISO-8601 timestamps. Use `broadcast_datetime` for exchange-stamped freshness (some filer-supplied dates like insider_trading `acq_to_date` contain future-year typos - do not use for latest-date logic). pledge_data is a snapshot table (latest quarter only, not historical time series).

**Navigation (machine-readable):** catalog https://api.tigzig.com/vigil/v1/ | data dictionary (tables + columns) https://api.tigzig.com/vigil/v1/tables | downloads https://api.tigzig.com/vigil/v1/download/all | human docs https://www.tigzig.com/apis/vigil

Author: Amar Harolikar - https://www.linkedin.com/in/amarharolikar

Rate limit: 30 requests/min per IP. Downloads: 10/min.

Part of Tigzig: free interactive tools, open-source repos, APIs and MCP servers for analytics and live data across global and Indian markets, macro indicators and filings. Catalog: https://api.tigzig.com/.well-known/api-catalog | Guide: https://www.tigzig.com/llms.txt

App: https://vigil.tigzig.com

**Source and licence.** The underlying data is published by its original sources as public records. This service reformats and republishes it, is not an authoritative source, and is not affiliated with or endorsed by any of them. No rights in the underlying data are claimed here, and none are granted. Check the original publisher's own terms before relying on it. To the extent this service holds any rights in the compilation (the assembly, the schema and the derived fields), they are released under CC0 1.0 (https://creativecommons.org/publicdomain/zero/1.0/); no attribution required. Provided as is. No guarantee of accuracy, completeness or availability, and no support commitment. Check against the original publisher for anything that matters.

## Licence, data source and warranty, as plain text

`GET /vigil/v1/terms`

The same three statements the whole API carries, as plain text you can save
or diff, rather than JSON you have to unwrap.

**Licence.** To the extent this service holds any rights in the compilation, meaning
the assembly, the schema and the derived fields, they are released under CC0 1.0. No
attribution required, though it is always appreciated.

**Source.** The underlying filings are published by their original
sources as public records, and this service reformats and republishes them. It is not
an authoritative source and is not affiliated with or endorsed by any of them. The licence above covers this
service's compilation of it, never the underlying records themselves. No rights in
those are claimed here and none are granted, so check the original publisher's own terms before
relying on it.

**No warranty.** Provided as is. No guarantee of accuracy, completeness or
availability, and no support commitment.

The same three statements also travel with the bulk downloads: in the manifest, and
in the footer metadata of the parquet files, so they survive being copied onward.

Returns `text/plain`, cached for a day.

## API catalog - list all available endpoints and tables

`GET /vigil/v1/`

Machine-readable catalog of every endpoint and table. Start here.

Returns each endpoint with its purpose, each table with its columns and row
count, and pointers to the OpenAPI spec and the human documentation. An agent
can read this one response and know the whole surface without being told.

### Where to go next

- `/vigil/v1/tables` - row counts, columns and freshness per table
- `/vigil/v1/company/{identifier}` - everything held on one company
- `/vigil/v1/data/{table_name}` - page through a single table
- `/vigil/v1/download/{table_name}` - a whole table as one file
- `/vigil/v1/openapi.json` - the full machine spec
- `/vigil/v1/redoc` - the same spec as a readable page

### What this API covers

Corporate red-flag signals for Indian listed companies, sourced from NSE and
SEBI filings: credit rating actions, insider trades, promoter pledging and
encumbrance, takeover filings, exchange surveillance stages, and related party
transactions. No authentication, no key, free to use.

Rate limit: **30 requests per minute per IP**.

## List all tables with row counts and freshness

`GET /vigil/v1/tables`

Row counts, column lists and last-sync time for all seven tables.

Start here if you are exploring. Every table name returned is a valid
`table_name` for `/data/{table_name}` and `/download/{table_name}`.

### What the seven tables hold

- `credit_ratings` - rating actions from every SEBI-registered agency, with the
  agency, instrument, rating, outlook and a derived `red_flag_reason`
- `insider_trading` - SEBI PIT disclosures: who traded, how much, at what value
- `pledge_data` - promoter share pledging, as a **current snapshot** per company
- `sast_disclosures` - substantial acquisition and takeover filings
- `encumbrance_events` - promoter encumbrance creation and release events
- `surveillance_flags` - NSE ASM, GSM and ESM surveillance stages, as a **daily
  snapshot**
- `rpt_transactions` - related party transactions from half-yearly filings

### Snapshot tables behave differently, and it matters

`pledge_data` and `surveillance_flags` are **snapshots, not history**. Every row
carries the same `sync_date`, and each refresh replaces the whole table. Asking
either of them "what changed last month" returns nothing useful.

The other five are **append-only history** and are safe to filter by date.

## List all companies with symbol and ISIN

`GET /vigil/v1/companies`

Every distinct company across all seven tables, with NSE symbol and ISIN.

Use this to resolve a name you have into an identifier `/company/{identifier}`
accepts, or to enumerate the universe before a batch job.

### Coverage note

The list is the union of companies appearing in **any** table, so it is wider
than the currently listed universe. It includes companies that have since been
delisted, suspended, or merged, because their historical filings are still in
the data. A company appearing here is not a statement that it is currently
listed or trading.

## All data for a single company

`GET /vigil/v1/company/{identifier}`

Every record held for one company, across all seven tables, in a single call.

This is the endpoint most callers want. One request returns the company's credit
rating actions, insider trades, pledge position, takeover filings, encumbrance
events, surveillance flags and related party transactions together.

### The identifier is forgiving

Preferred is the **NSE symbol**, which is unambiguous:

    /vigil/v1/company/RELIANCE

Also accepted:

- **ISIN** - `INE002A01018`
- **Company name** - `Reliance Industries Limited`

Name and symbol matching ignores case, punctuation and spacing, and treats `Ltd`
and `Limited` as the same word. All four of these resolve to the same company:

    JBCHEPHARM
    jbchepharm
    JB Chemicals & Pharmaceuticals Limited
    J.B. Chemicals & Pharmaceuticals Ltd.

Deliberately **not** merged: `X Private Limited` stays distinct from `X Limited`,
because an unlisted subsidiary and its listed parent are different companies.

#### Prefer the symbol, because ISIN reaches only one table

Only `credit_ratings` carries an ISIN column. The other six tables are keyed on
symbol and company name, so an ISIN lookup returns credit ratings and **nothing
else**, with a `200` and no warning. For `RELIANCE` that is 55 records against
5,727 by symbol.

Use the ISIN to find the company, then query by its symbol.

### Narrowing the response

A full company payload can be large, and it is usually one table making it large.
An unfiltered `RELIANCE` is about **5.9 MB**, of which `rpt_transactions` is 5,662
of the 5,727 records. Request only the tables you need:

    /vigil/v1/company/RELIANCE?tables=credit_ratings,insider_trading

That is 63 records and about 59 KB. Read `summary` first if you want to see what
exists before pulling it - an unfiltered call returns the counts and the rows
together, so there is no cheap way to look before you fetch.

### Response shape

- `identifier` - the value you sent, echoed back
- `summary` - record count per table, so you can see what exists before reading it
- `total_records` - sum across all tables
- `sections` - the actual rows, keyed by table name

`total_records: 0` means the identifier resolved to nothing. It is not an error,
so no 404 is raised. Check `/companies` if you expected a match.

### Parameters

- `format` - `json` (default) or `tsv`. Any other value is rejected with `400`
- Rate limit: **30 requests per minute per IP**

## Paginated table data

`GET /vigil/v1/data/{table_name}`

Paginated rows from one table, for browsing or for pulling a slice.

Use this to page through a table. To take a **whole** table, use
`/download/{table_name}` instead - it serves a pre-generated file and is far
cheaper for both sides than paging through hundreds of thousands of rows.

### Valid table names

`credit_ratings`, `insider_trading`, `pledge_data`, `sast_disclosures`,
`encumbrance_events`, `surveillance_flags`, `rpt_transactions`

An unknown name returns `400` with the full list, so you never have to guess.

### Pagination

- `limit` - rows per page. Default **1000**, maximum **5000**
- `offset` - row offset, default `0`
- `total`, `has_more` and `next_offset` come back in every response, so a client
  can loop on `has_more` without tracking its own counter

### Parameters

- `format` - `json` (default) or `tsv`. Any other value is rejected with `400`
- Rate limit: **30 requests per minute per IP**

### One caveat on long sweeps

Paging is not a snapshot. The syncs run four times a day, and a long sweep can
cross one, so a row may shift page between requests. If you need a consistent
point-in-time view, take the download file, which is generated atomically.

## Download all tables as a single file

`GET /vigil/v1/download/all`

All seven tables in one SQLite database, ready to query with joins.

The whole dataset as a single file, about 475,000 rows across seven tables. Use
this when you want to work across tables locally rather than pull each one.

### Formats

- `sqlite` - zipped. Unzip to get `vigil_all_tables.db`
- `sqlite.gz` - gzipped, for scripts

`sqlite.zip`, `db.zip` and `db.gz` are accepted as aliases. Anything else
returns `400`.

### Why the combined file is useful

Every table keys on `symbol`, so one file lets you ask cross-table questions
without joining across HTTP calls:

```sql
SELECT s.symbol, s.gsm_stage, p.perc_shares_pledged
FROM surveillance_flags s
JOIN pledge_data p ON p.nse_symbol = s.symbol
WHERE p.perc_shares_pledged > 50;
```

Column types are preserved, so numeric comparisons like the one above behave
correctly rather than sorting as text.

Sizes and generation time: `/vigil/v1/downloads/manifest`. Supports `HEAD`.

## Download all tables as a single file

`HEAD /vigil/v1/download/all`

All seven tables in one SQLite database, ready to query with joins.

The whole dataset as a single file, about 475,000 rows across seven tables. Use
this when you want to work across tables locally rather than pull each one.

### Formats

- `sqlite` - zipped. Unzip to get `vigil_all_tables.db`
- `sqlite.gz` - gzipped, for scripts

`sqlite.zip`, `db.zip` and `db.gz` are accepted as aliases. Anything else
returns `400`.

### Why the combined file is useful

Every table keys on `symbol`, so one file lets you ask cross-table questions
without joining across HTTP calls:

```sql
SELECT s.symbol, s.gsm_stage, p.perc_shares_pledged
FROM surveillance_flags s
JOIN pledge_data p ON p.nse_symbol = s.symbol
WHERE p.perc_shares_pledged > 50;
```

Column types are preserved, so numeric comparisons like the one above behave
correctly rather than sorting as text.

Sizes and generation time: `/vigil/v1/downloads/manifest`. Supports `HEAD`.

## Download full table as a file

`GET /vigil/v1/download/{table_name}`

A whole table as one pre-generated file. The right way to take bulk data.

Files are rebuilt after each sync and served straight from static storage, so a
download does not queue behind a query. Prefer this over paging
`/data/{table_name}` for anything larger than a few pages.

### Choosing a format

| Format | Use it for |
|---|---|
| `csv` | Universal. Zipped, so a browser handles it |
| `csv.gz` | The same data, gzipped, for scripts and pipelines |
| `tsv` | Tab-separated. Cleaner than CSV when values contain commas |
| `tsv.gz` | Gzipped TSV |
| `parquet` | Columnar and typed. Best for pandas, Polars and DuckDB |
| `sqlite` | Query locally with SQL, no import step. Zipped |
| `sqlite.gz` | The same database, gzipped |

Anything else returns `400` listing the valid formats.

### Column types survive in parquet and sqlite

Both carry real column types, so numeric comparisons behave:

```sql
SELECT MAX(no_of_securities) FROM insider_trading;
```

Worth stating because it was not always true. Until 2026-08-11 the SQLite exports
declared every column `TEXT`, which made `MAX`, `ORDER BY` and `WHERE col > n`
sort lexicographically and return confident wrong answers with no error. Fixed,
and verified across every table. `csv` and `tsv` are untyped by nature, so the
parser at your end decides.

### Sizes and freshness

`/vigil/v1/downloads/manifest` gives the byte size, row count and generation time
of every file. Supports `HEAD` for a size check without the body.

## Download full table as a file

`HEAD /vigil/v1/download/{table_name}`

A whole table as one pre-generated file. The right way to take bulk data.

Files are rebuilt after each sync and served straight from static storage, so a
download does not queue behind a query. Prefer this over paging
`/data/{table_name}` for anything larger than a few pages.

### Choosing a format

| Format | Use it for |
|---|---|
| `csv` | Universal. Zipped, so a browser handles it |
| `csv.gz` | The same data, gzipped, for scripts and pipelines |
| `tsv` | Tab-separated. Cleaner than CSV when values contain commas |
| `tsv.gz` | Gzipped TSV |
| `parquet` | Columnar and typed. Best for pandas, Polars and DuckDB |
| `sqlite` | Query locally with SQL, no import step. Zipped |
| `sqlite.gz` | The same database, gzipped |

Anything else returns `400` listing the valid formats.

### Column types survive in parquet and sqlite

Both carry real column types, so numeric comparisons behave:

```sql
SELECT MAX(no_of_securities) FROM insider_trading;
```

Worth stating because it was not always true. Until 2026-08-11 the SQLite exports
declared every column `TEXT`, which made `MAX`, `ORDER BY` and `WHERE col > n`
sort lexicographically and return confident wrong answers with no error. Fixed,
and verified across every table. `csv` and `tsv` are untyped by nature, so the
parser at your end decides.

### Sizes and freshness

`/vigil/v1/downloads/manifest` gives the byte size, row count and generation time
of every file. Supports `HEAD` for a size check without the body.

## Data freshness - last sync time per table

`GET /vigil/v1/freshness`

Last sync time and record count per table, for checking how current the data is.

### Refresh cadence

- `credit_ratings`, `insider_trading`, `sast_disclosures`, `encumbrance_events`,
  `pledge_data`, `surveillance_flags` sync **four times a day**
- `rpt_transactions` syncs **weekly**, because the underlying filings are
  half-yearly and the scan is expensive

### Reading the result honestly

A recent sync time means the pipeline ran, **not** that new records arrived. NSE
publishes on trading days only, so an unchanged count over a weekend or a market
holiday is correct behaviour rather than a stall. Compare `last_sync` against the
record count over several days before concluding anything is stuck.

## Download file manifest with sizes

`GET /vigil/v1/downloads/manifest`

File sizes, row counts and generation time for every pre-generated download.

Read this before offering a download so you can show a size, and to check how
fresh the files are without fetching them.

### Fields worth using

- `generated_at` - UTC timestamp of the last regeneration. Files are rebuilt
  after each data sync, so this trails `/freshness` by a few minutes
- `files` - keyed by filename, each with `size_bytes`, `size_human`, `format`
  and `row_count`
- `tables` - authoritative row count per table at generation time
- `validation.all_match` - `true` when every generated file's row count agrees
  with its source table. Treat `false` as a reason not to trust that batch

Supports `HEAD` if you only need to confirm the endpoint is alive.

Every 200 carries `ETag` and `Last-Modified`, and this path honours
`If-None-Match` / `If-Modified-Since` with `304 Not Modified` and an empty
body. Poll it conditionally: the files regenerate 4 times a day, so an
unchanged manifest costs almost nothing to check.

## Download file manifest with sizes

`HEAD /vigil/v1/downloads/manifest`

File sizes, row counts and generation time for every pre-generated download.

Read this before offering a download so you can show a size, and to check how
fresh the files are without fetching them.

### Fields worth using

- `generated_at` - UTC timestamp of the last regeneration. Files are rebuilt
  after each data sync, so this trails `/freshness` by a few minutes
- `files` - keyed by filename, each with `size_bytes`, `size_human`, `format`
  and `row_count`
- `tables` - authoritative row count per table at generation time
- `validation.all_match` - `true` when every generated file's row count agrees
  with its source table. Treat `false` as a reason not to trust that batch

Supports `HEAD` if you only need to confirm the endpoint is alive.

Every 200 carries `ETag` and `Last-Modified`, and this path honours
`If-None-Match` / `If-Modified-Since` with `304 Not Modified` and an empty
body. Poll it conditionally: the files regenerate 4 times a day, so an
unchanged manifest costs almost nothing to check.
