Users, brokers and exchanges¶
These three routes serve reference information that changes rarely: the account holder's profile, each broker's registration and contact details, and each exchange's profile with its trading hours and holidays. None of them calls a broker while you wait. They read documents that were loaded into MongoDB by hand and that background scripts copy into Redis once a minute.
The table below lists the three routes on this page.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/users/details |
The account holder's profile documents, and each broker's own profile of the account |
| GET | /api/brokers/details |
One document per broker |
| GET | /api/exchanges/details |
One document per exchange, with trading hours, holidays and special sessions |
Where the documents come from¶
Three MongoDB collections hold these documents, and MongoDB is the store of record for all of them. They are filled by bin/import-api-details, which reads MongoDB Compass exports from a directory you name, and they are only ever changed by running that script again.
The diagram below shows how a document travels from an export file to an API response. The Redis copy is only a speed-up: when it is missing or unreadable, the route reads MongoDB directly and serves the same documents.
flowchart LR
X["Compass exports<br/>and calendar YAML"] -->|bin/import-api-details| M[("MongoDB<br/>user_details<br/>broker_details<br/>exchange_details")]
M -->|"every minute<br/>bin/unified/*/unified_details"| R[("Redis<br/>unified:details:users<br/>unified:details:brokers<br/>unified:details:exchanges")]
R --> A["GET /api/*/details"]
M -. "copy missing or unreadable" .-> A
Each collection has its own copying script, its own Redis key and its own route, as the table below shows.
| Collection | Export file | Keyed by | Copied by | Redis key | Served by |
|---|---|---|---|---|---|
user_details |
unified_broker_interface.user_details.json |
nothing | bin/unified/user/unified_details |
unified:details:users |
GET /api/users/details |
broker_details |
mongo_backup/unified_broker_interface.brokers.json |
broker_name |
bin/unified/brokers/unified_details |
unified:details:brokers |
GET /api/brokers/details |
exchange_details |
unified_broker_interface.exchanges.json |
exchange |
bin/unified/exchanges/unified_details |
unified:details:exchanges |
GET /api/exchanges/details |
Because the three copying scripts run separately, one key can already hold a new import while another still holds the copy from the minute before. No route reads more than one of them, so nothing depends on them changing together. A change to MongoDB reaches the API within about a minute.
The import makes two corrections on the way in, which explain some of the field names you will see.
- Exchanges get an
exchangefield, the lowercase code used everywhere else in the project (nse,bse,mcx,ncdex), taken from the export's displayshort_namesuch asNSE. - Brokers arrive keyed by a misspelled
borker_nameholding a display name such asKotak Neo (Kotak Securities Limited). The import keeps that display name asnameand setsbroker_nameto the project's broker code, such askotak. A display name it does not recognize stops the import instead of being guessed.
User details hold personal data
The user_details export holds identity documents and bank account numbers. The import reads the file where it is and never copies it into the repository, and the route serves it only to a caller holding the access token.
The commands below are the ways to run the import.
bin/import-api-details /path/to/exports # upsert exchanges and brokers, load users if empty, copy calendars
bin/import-api-details /path/to/exports --replace # also reload user_details over what is there
bin/import-api-details --calendars-only # only re-copy trading hours and holidays
User details¶
GET /api/users/detailsaccess-token
This route returns two things side by side. The first is every document in user_details, which is the account holder's profile as it was imported. The second is each broker's own profile of the account, which bin/unified/user/details gathers once a minute from the profiles that each broker's scripts keep in Redis.
Request parameters¶
| Name | In | Type | Required | Description |
|---|---|---|---|---|
access-token |
header | string | Yes | The token from connect |
Example¶
Response¶
The example below is shortened and uses placeholder values. The fields inside user_details are whatever the import file held, so they are not listed here. Each broker's profile is that broker's own response body, in that broker's own field names.
{
"user_details": [
{"...": "the fields of the imported profile"}
],
"broker_profiles": {
"dhan": {"dhanClientId": "AB1234", "activeSegment": "...", "...": "..."},
"zerodha": {"user_id": "AB1234", "...": "..."},
"flattrade": null,
"...": "one key for each of the ten brokers"
}
}
Response attributes¶
| Attribute | Type | Description |
|---|---|---|
user_details |
array of objects | Every document in the user_details collection, without _id, in MongoDB's natural order |
broker_profiles |
object or null | One key per broker, each holding that broker's profile or null. The whole value is null when unified:user:details is missing or unreadable. |
A broker's profile is null when its key is missing or does not hold a profile. The table below shows how often each broker's profile is refreshed, because that decides how fresh this route's copy can be.
| Broker | Kept by | Refreshed |
|---|---|---|
| dhan, zerodha, flattrade, shoonya, fyers, indmoney, groww, stoxkart | bin/<broker>/user/details |
Every minute |
| wisdom_capital | bin/wisdom_capital/user/details |
Once a day, as Wisdom Capital allows about one profile call a day |
| kotak | bin/kotak/session/connect, from the login response |
At each login, because Kotak has no profile endpoint |
Status codes¶
| Status | When |
|---|---|
| 200 | At least one user document or one broker profile exists. |
| 401 | Access token is required, Invalid access token or Access token has expired. |
| 404 | User profile not found: there is no user document and no broker profile. |
Under the hood
- Route:
UsersBlueprint.get_user_profileinunified_broker_interface/blueprints/users.py. - Documents:
BaseBlueprint.collection_documentsreadsunified:details:usersand falls back to MongoDBuser_details. - Broker profiles: the Redis key
unified:user:details, written withSETevery minute bybin/unified/user/detailsfrom the ten<broker>:user:detailskeys, read in oneMGET.
Broker details¶
GET /api/brokers/detailsaccess-token
This route returns every document in broker_details, one per broker. Each document carries the project's broker code in broker_name, the display name in name, and the other registration and contact fields from the import file.
Request parameters¶
| Name | In | Type | Required | Description |
|---|---|---|---|---|
access-token |
header | string | Yes | The token from connect |
Example¶
Response¶
The example below is shortened to two brokers and shows only the two fields the import guarantees.
[
{"broker_name": "kotak", "name": "Kotak Neo (Kotak Securities Limited)"},
{"broker_name": "shoonya", "name": "Shoonya (Finvasia)"}
]
Response attributes¶
The table below lists the fields the import sets. The rest of each document is copied from the export as it stands.
| Attribute | Type | Description |
|---|---|---|
broker_name |
string | The project's broker code, one of the ten listed in Constants |
name |
string | The broker's display name, as the export spelled it |
The import maps display names to broker codes with the fixed table below.
| Display name in the export | broker_name |
|---|---|
dhan |
dhan |
flattrade |
flattrade |
fyers |
fyers |
Groww |
groww |
INDstocks |
indmoney |
Kotak Neo (Kotak Securities Limited) |
kotak |
Shoonya (Finvasia) |
shoonya |
Stoxkart |
stoxkart |
Wisdom Capital |
wisdom_capital |
Zerodha |
zerodha |
Status codes¶
| Status | When |
|---|---|
| 200 | The collection holds at least one document. |
| 401 | Access token is required, Invalid access token or Access token has expired. |
| 404 | Broker details not found: the collection is empty. |
Under the hood
- Route:
BrokersBlueprint.get_broker_detailsinunified_broker_interface/blueprints/brokers.py, which callsBaseBlueprint.list_collection. - Import:
normalise_brokerinunified_broker_interface/utilities/import_details.py. Documents are upserted onbroker_nameunder a unique index, and fields are set rather than documents replaced.
Exchange details¶
GET /api/exchanges/detailsaccess-token
This route returns every document in exchange_details, one per exchange. Besides the profile fields from the import file, each document carries the exchange's trading hours, its holidays and its special sessions, which the import copies in from the project's yearly calendar files.
Request parameters¶
| Name | In | Type | Required | Description |
|---|---|---|---|---|
access-token |
header | string | Yes | The token from connect |
Example¶
Response¶
The example below shows one exchange, shortened to one holiday per calendar and without the profile fields beyond exchange and short_name. The holidays and the special session are real entries from stock_brokers/instruments/ticks/utilities/calendars/2026.yaml.
[
{
"exchange": "nse",
"short_name": "NSE",
"trading_hours": {
"timezone": "Asia/Kolkata",
"equity": {
"pre_open": {"opens": "09:00", "closes": "09:15"},
"sessions": [{"name": "normal", "opens": "09:15", "closes": "15:30"}]
},
"currency": {
"sessions": [{"name": "normal", "opens": "09:00", "closes": "17:00"}]
},
"commodity": {
"sessions": [
{"name": "morning", "opens": "09:00", "closes": "17:00"},
{"name": "evening", "opens": "17:00", "closes": "23:30", "closes_during_us_standard_time": "23:55"}
]
}
},
"holidays": {
"equity": [{"date": "2026-01-26", "closed": "all", "name": "Republic Day"}],
"currency": [{"date": "2026-01-26", "closed": "all", "name": "Republic Day"}],
"commodity": [{"date": "2026-01-26", "closed": "all", "name": "Republic Day"}]
},
"special_sessions": [
{"date": "2026-11-08", "name": "Muhurat trading (timings to be notified)",
"calendars": ["equity", "currency", "commodity"], "opens": "09:00", "closes": "23:59:59"}
],
"calendar_years": [2026],
"calendar_copied_at": "2026-09-13 19:50:02.114210"
}
]
Response attributes¶
The table below lists the fields the import adds. The other profile fields are copied from the export as it stands.
| Attribute | Type | Description |
|---|---|---|
exchange |
string | The lowercase exchange code: nse, bse, mcx or ncdex |
short_name |
string | The display code the export carried, such as NSE |
trading_hours |
object | timezone (always Asia/Kolkata) and one entry per calendar the exchange trades |
trading_hours.<calendar>.pre_open |
object | opens and closes of the pre-open, present only for equity |
trading_hours.<calendar>.sessions |
array | Each session's name, opens and closes, as HH:MM |
holidays |
object | One list per calendar, each entry date (YYYY-MM-DD), closed and name, sorted by date |
special_sessions |
array | Extra sessions such as Muhurat trading: date, name, calendars, opens, closes |
calendar_years |
array of integers | The years whose calendar files were read |
calendar_copied_at |
string | When the calendars were last copied in |
A calendar is a set of segments that open and close together. The table below lists the calendars and which exchanges have them.
| Calendar | What it covers | Exchanges |
|---|---|---|
equity |
Cash, indices and equity derivatives | nse, bse |
currency |
Currency derivatives | nse, bse |
commodity |
Commodity derivatives | nse, bse, mcx, ncdex |
A holiday's closed is all when the whole day is shut. On the commodity calendars it can also be morning or evening, because their trading day splits into two sessions at 17:00.
The chart below draws each calendar's sessions on one 24-hour axis, so you can see at a glance when each market is open. The hours come from TRADING_HOURS in unified_broker_interface/utilities/exchange_calendar.py.
gantt
title Regular trading hours (IST)
dateFormat HH:mm
axisFormat %H:%M
section NSE, BSE equity
Pre-open :09:00, 15m
Normal :09:15, 375m
section NSE, BSE currency
Normal :09:00, 480m
section NSE, BSE, MCX commodity
Morning :09:00, 480m
Evening :17:00, 390m
section NCDEX commodity
Morning :10:00, 420m
Evening :17:00, 240m
The commodity evening session on NSE, BSE and MCX closes at 23:30, and at 23:55 while the United States is on standard time. That later close appears in the document as closes_during_us_standard_time.
Re-copy after a calendar change
The API serves the copy in MongoDB, not the YAML file. After a calendar file changes, run bin/import-api-details --calendars-only, and the change reaches the API within a minute.
Status codes¶
| Status | When |
|---|---|
| 200 | The collection holds at least one document. |
| 401 | Access token is required, Invalid access token or Access token has expired. |
| 404 | Exchange details not found: the collection is empty. |
Under the hood
- Route:
ExchangesBlueprint.get_exchange_detailsinunified_broker_interface/blueprints/exchanges.py, which callsBaseBlueprint.list_collection. - Import:
normalise_exchangeinunified_broker_interface/utilities/import_details.pyaddsexchange. - Calendars:
copy_exchange_calendarsmerges every<year>.yamlunderstock_brokers/instruments/ticks/utilities/calendars/and sets only its own fields, so a re-import of the profiles leaves them in place. It refuses to run until every exchange inTRADING_HOURShas a document. - The hours here are the sessions' real hours. They are not the wider windows the tick pipeline accepts ticks in.