├── .gitignore ├── cover.png ├── package.json ├── api ├── client.js ├── indices.js ├── sectors.js └── index.js ├── README.md ├── yarn.lock └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vercel 3 | node_modules -------------------------------------------------------------------------------- /cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/risan/indonesia-stock-exchange/HEAD/cover.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "axios": "^0.21.1", 4 | "yup": "^0.32.8" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /api/client.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | module.exports = axios.create({ 4 | baseURL: 'https://pasardana.id/api/', 5 | headers: { 6 | Accept: 'application/json', 7 | 'Accept-Encoding': 'gzip', 8 | Host: 'pasardana.id', 9 | Pragma: 'no-cache', 10 | Referer: 'https://pasardana.id/stock/search', 11 | 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0', 12 | }, 13 | }); -------------------------------------------------------------------------------- /api/indices.js: -------------------------------------------------------------------------------- 1 | const client = require('./client'); 2 | 3 | module.exports = async (req, res) => { 4 | try { 5 | const response = await client.get('/Stock/GetIndexFilters'); 6 | 7 | const data = response.data.map((i, idx) => ({ 8 | id: i.Id, 9 | name: i.Name, 10 | stocks_url: `https://indonesia-stock-exchange.vercel.app/api?index_id=${i.Id}`, 11 | })); 12 | 13 | return res.json({ data }); 14 | } catch (error) { 15 | return res.status(500).json({ error: error.message }); 16 | } 17 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Indonesia Stock Exchange API 2 | 3 | API to get Indonesia stock exchange daily data from [Pasardana](https://pasardana.id/) website. 4 | 5 | ## Installation 6 | 7 | You'll need a [Vercel](https://vercel.com/home) account and [Vercel CLI](https://vercel.com/download) to run this API. 8 | 9 | ```bash 10 | # Clone the repository 11 | $ git clone git@github.com:risan/indonesia-stock-exchange.git 12 | 13 | # Install dependencies 14 | $ npm install 15 | 16 | # Spin up vercel development 17 | $ vercel dev 18 | ``` 19 | 20 | Checkout [indonesia-stock-exchange.vercel.app](https://indonesia-stock-exchange.vercel.app/) for API documentation. -------------------------------------------------------------------------------- /api/sectors.js: -------------------------------------------------------------------------------- 1 | const client = require('./client'); 2 | 3 | const getSubsectors = async sectorId => { 4 | const response = await client.get('/StockSubSector/GetSubSectorFromSector', { 5 | params: { sectorId }, 6 | }); 7 | 8 | return response.data; 9 | }; 10 | 11 | module.exports = async (req, res) => { 12 | try { 13 | const response = await client.get('/StockSector/GetAll'); 14 | 15 | const subSectors = await Promise.all(response.data.map(i => getSubsectors(i.Id))); 16 | 17 | const data = response.data.map((sector, idx) => ({ 18 | id: sector.Id, 19 | name: sector.Name, 20 | stocks_url: `https://indonesia-stock-exchange.vercel.app/api?sector_id=${sector.Id}`, 21 | subsectors: subSectors[idx].map(subsector => ({ 22 | id: subsector.Id, 23 | name: subsector.Name, 24 | stocks_url: `https://indonesia-stock-exchange.vercel.app/api?subsector_id=${subsector.Id}`, 25 | })), 26 | })); 27 | 28 | return res.json({ data }); 29 | } catch (error) { 30 | return res.status(500).json({ error: error.message }); 31 | } 32 | }; -------------------------------------------------------------------------------- /api/index.js: -------------------------------------------------------------------------------- 1 | const yup = require('yup'); 2 | const client = require('./client'); 3 | 4 | const SORTABLE_FIELDS = [ 5 | 'Code', 6 | 'Name', 7 | 'SectorName', 8 | 'SubSectorName', 9 | 'Last', 10 | 'AdjustedClosingPrice', 11 | 'PrevClosingPrice', 12 | 'AdjustedOpenPrice', 13 | 'AdjustedHighPrice', 14 | 'AdjustedLowPrice', 15 | 'Per', 16 | 'Pbr', 17 | 'Volume', 18 | 'Value', 19 | 'Frequency', 20 | 'OneDay', 21 | 'OneWeek', 22 | 'OneMonth', 23 | 'ThreeMonth', 24 | 'SixMonth', 25 | 'OneYear', 26 | 'ThreeYear', 27 | 'FiveYear', 28 | 'TenYear', 29 | 'Mtd', 30 | 'Ytd', 31 | 'Capitalization', 32 | 'BetaOneYear', 33 | 'StdevOneYear', 34 | 'LastDate', 35 | ]; 36 | 37 | const validate = async (params) => { 38 | const schema = yup.object().shape({ 39 | search: yup.string().trim(), 40 | page: yup.number().positive().integer().default(1), 41 | per_page: yup.number().positive().integer().default(25), 42 | sort_by: yup.string().trim().oneOf(SORTABLE_FIELDS).default('Code'), 43 | sort_direction: yup.string().trim().lowercase().oneOf(['asc', 'desc']).default('asc'), 44 | index_id: yup.number().positive().integer(), 45 | sector_id: yup.number().positive().integer(), 46 | subsector_id: yup.number().positive().integer(), 47 | }); 48 | 49 | await schema.validate(params); 50 | 51 | const data = schema.cast(params); 52 | 53 | return { 54 | Keywords: data.search, 55 | pageBegin: data.page, 56 | pageLength: data.per_page, 57 | sortField: data.sort_by, 58 | sortOrder: data.sort_direction.toUpperCase(), 59 | Index: data.index_id, 60 | StockSector: data.sector_id, 61 | StockSubSector: data.subsector_id, 62 | }; 63 | }; 64 | 65 | const getData = async (params = {}) => { 66 | const parsedParams = await validate(params); 67 | 68 | try { 69 | const response = await client.get('/StockSearchResult/GetAll', { 70 | params: parsedParams, 71 | }); 72 | 73 | return response.data; 74 | } catch (error) { 75 | const message = error.response ? error.response.data.message : error.message; 76 | 77 | throw new Error(message); 78 | } 79 | }; 80 | 81 | module.exports = async (req, res) => { 82 | try { 83 | const data = await getData(req.query); 84 | 85 | return res.json({ data }); 86 | } catch (error) { 87 | const status = error.name === 'ValidationError' ? 422 : 500; 88 | 89 | return res.status(status).json({ error: error.message }); 90 | } 91 | }; -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/runtime@^7.10.5": 6 | version "7.12.5" 7 | resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" 8 | integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== 9 | dependencies: 10 | regenerator-runtime "^0.13.4" 11 | 12 | "@types/lodash@^4.14.165": 13 | version "4.14.167" 14 | resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.167.tgz#ce7d78553e3c886d4ea643c37ec7edc20f16765e" 15 | integrity sha512-w7tQPjARrvdeBkX/Rwg95S592JwxqOjmms3zWQ0XZgSyxSLdzWaYH3vErBhdVS/lRBX7F8aBYcYJYTr5TMGOzw== 16 | 17 | axios@^0.21.1: 18 | version "0.21.1" 19 | resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" 20 | integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== 21 | dependencies: 22 | follow-redirects "^1.10.0" 23 | 24 | follow-redirects@^1.10.0: 25 | version "1.13.1" 26 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7" 27 | integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg== 28 | 29 | lodash-es@^4.17.11: 30 | version "4.17.20" 31 | resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.20.tgz#29f6332eefc60e849f869c264bc71126ad61e8f7" 32 | integrity sha512-JD1COMZsq8maT6mnuz1UMV0jvYD0E0aUsSOdrr1/nAG3dhqQXwRRgeW0cSqH1U43INKcqxaiVIQNOUDld7gRDA== 33 | 34 | lodash@^4.17.20: 35 | version "4.17.20" 36 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" 37 | integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== 38 | 39 | nanoclone@^0.2.1: 40 | version "0.2.1" 41 | resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4" 42 | integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA== 43 | 44 | property-expr@^2.0.4: 45 | version "2.0.4" 46 | resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.4.tgz#37b925478e58965031bb612ec5b3260f8241e910" 47 | integrity sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg== 48 | 49 | regenerator-runtime@^0.13.4: 50 | version "0.13.7" 51 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" 52 | integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== 53 | 54 | toposort@^2.0.2: 55 | version "2.0.2" 56 | resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" 57 | integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA= 58 | 59 | yup@^0.32.8: 60 | version "0.32.8" 61 | resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.8.tgz#16e4a949a86a69505abf99fd0941305ac9adfc39" 62 | integrity sha512-SZulv5FIZ9d5H99EN5tRCRPXL0eyoYxWIP1AacCrjC9d4DfP13J1dROdKGfpfRHT3eQB6/ikBl5jG21smAfCkA== 63 | dependencies: 64 | "@babel/runtime" "^7.10.5" 65 | "@types/lodash" "^4.14.165" 66 | lodash "^4.17.20" 67 | lodash-es "^4.17.11" 68 | nanoclone "^0.2.1" 69 | property-expr "^2.0.4" 70 | toposort "^2.0.2" 71 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Indonesia Stock Exchange API 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 33 | 34 | 35 |
36 |

37 | 📈 38 | Indonesia Stock Exchange API 39 |

40 |

41 | API to get Indonesia stock exchange daily data from Pasardana website. 42 |

43 | 44 |

1. List of Stocks

45 |
https://indonesia-stock-exchange.vercel.app/api
46 | 47 |

Query Parameter

48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 63 | 64 | 65 | 66 | 70 | 71 | 72 | 73 | 77 | 78 | 79 | 80 | 114 | 115 | 116 | 117 | 121 | 122 | 123 | 124 | 129 | 130 | 131 | 132 | 137 | 138 | 139 | 140 | 145 | 146 | 147 |
ParameterDescription
search 60 | Type: string
61 | Search query 62 |
page 67 | Type: int | Default: 1
68 | Page to retrieve 69 |
per_page 74 | Type: int | Default: 25
75 | Number of records per page 76 |
sort_by 81 | Type: string | Default: Code
82 | Sort the results by the given field. Possible values: 83 | Code, 84 | Name, 85 | SectorName, 86 | SubSectorName, 87 | Last, 88 | AdjustedClosingPrice, 89 | PrevClosingPrice, 90 | AdjustedOpenPrice, 91 | AdjustedHighPrice, 92 | AdjustedLowPrice, 93 | Per, 94 | Pbr, 95 | Volume, 96 | Value, 97 | Frequency, 98 | OneDay, 99 | OneWeek, 100 | OneMonth, 101 | ThreeMonth, 102 | SixMonth, 103 | OneYear, 104 | ThreeYear, 105 | FiveYear, 106 | TenYear, 107 | Mtd, 108 | Ytd, 109 | Capitalization, 110 | BetaOneYear, 111 | StdevOneYear, 112 | LastDate, 113 |
sort_direction 118 | Type: string | Default: asc
119 | Sort direction: asc or desc. 120 |
index_id 125 | Type: int | Default:
126 | Filter stocks by the given index id. 127 | Get all possible index ids from the List Indices endpoint. 128 |
sector_id 133 | Type: int | Default:
134 | Filter stocks by the given sector id. 135 | Get all possible sector ids from the List Sectors endpoint. 136 |
subsector_id 141 | Type: int | Default:
142 | Filter stocks by the given subsector id. 143 | Get all possible subsector ids from the List Sectors endpoint. 144 |
148 | 149 |

2. List Indices

150 |

151 | List all stock market indices. 152 |

153 |
https://indonesia-stock-exchange.vercel.app/api/indices
154 | 155 |

3. List Sectors

156 |

157 | List all stocks' sectors and subsectors. 158 |

159 |
https://indonesia-stock-exchange.vercel.app/api/sectors
160 | 161 |

162 | By Risan Bagja | Source Code 163 |

164 |
165 | 166 | --------------------------------------------------------------------------------