Skip to content

Commit ca15811

Browse files
added backoff strategy to handle throttle errors
added CUSIP/CIK/Ticker Mapping API support
1 parent 1b33eac commit ca15811

File tree

5 files changed

+215
-18
lines changed

5 files changed

+215
-18
lines changed

README.md

+76
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,82 @@ print(filing)
425425
> See the documentation for more details: https://sec-api.io/docs/sec-filings-render-api
426426
427427

428+
# CUSIP/CIK/Ticker Mapping API
429+
430+
Resolve a CUSIP, CIK, ticker symbol or company name to a set of standardized company details. Listing companies by exchange, sector and industry is also supported.
431+
432+
Map any of the following parameters to company details:
433+
- CUSIP
434+
- CIK
435+
- Ticker
436+
- Company name
437+
- Exchange
438+
- Sector
439+
- Industry
440+
441+
The function returns an array of all matching companies in JSON format. For example, a look up of the ticker `IBM` returns multiple matches including `IBMD` and `IBME`.
442+
443+
A company object includes the following properties:
444+
445+
- `name` (string) - the name of the company, e.g. Tesla Inc
446+
- `ticker` (string) - the ticker symbol of the company.
447+
- `cik` (string) - the CIK of the company. Trailing zeros are removed.
448+
- `cusip` (string) - one or multiple CUSIPs linked to the company. Multiple CUSIPs are delimited by space, e.g. "054748108 92931L302 92931L401"
449+
- `exchange` (string) - the main exchange the company is listed on, e.g. NASDAQ
450+
- `isDelisted` (boolean) - true if the company is no longer listed, false otherwise.
451+
- `category` (string) - the security category, e.g. "Domestic Common Stock"
452+
- `sector` (string) - the sector of the company, e.g. "Consumer Cyclical"
453+
- `industry` (string) - the industry of the company, e.g. "Auto Manufacturers"
454+
- `sic` (string) - four-digit SIC code, e.g. "3711"
455+
- `sicSector` (string) - SIC sector name of the company, e.g. "Manufacturing"
456+
- `sicIndustry` (string) - SIC industry name of the company, e.g. "Motor Vehicles & Passenger Car Bodies"
457+
- `currency` (string) - operating currency of the company, e.g. "USD"
458+
- `location` (string) - location of the company's headquarters
459+
- `id` (string) - unique internal ID of the company, e.g. "e27d6e9606f216c569e46abf407685f3"
460+
461+
Response type: `JSON`
462+
463+
## Usage
464+
465+
```python
466+
from sec_api import MappingApi
467+
468+
mappingApi = MappingApi(api_key="YOUR_API_KEY")
469+
470+
result1 = mappingApi.resolve("ticker", "TSLA")
471+
result2 = mappingApi.resolve("cik", "1318605")
472+
result3 = mappingApi.resolve("cusip", "88160R101")
473+
result4 = mappingApi.resolve("exchange", "NASDAQ")
474+
```
475+
476+
### Response Example
477+
```json
478+
[
479+
{
480+
"name": "Tesla Inc",
481+
"ticker": "TSLA",
482+
"cik": "1318605",
483+
"cusip": "88160R101",
484+
"exchange": "NASDAQ",
485+
"isDelisted": false,
486+
"category": "Domestic Common Stock",
487+
"sector": "Consumer Cyclical",
488+
"industry": "Auto Manufacturers",
489+
"sic": "3711",
490+
"sicSector": "Manufacturing",
491+
"sicIndustry": "Motor Vehicles & Passenger Car Bodies",
492+
"famaSector": "",
493+
"famaIndustry": "Automobiles and Trucks",
494+
"currency": "USD",
495+
"location": "California; U.S.A",
496+
"id": "e27d6e9606f216c569e46abf407685f3"
497+
}
498+
]
499+
```
500+
501+
> See the documentation for more details: https://sec-api.io/docs/mapping-api
502+
503+
428504
# Query API Response Format
429505

430506
- `accessionNo` (string) - Accession number of filing, e.g. 0000028917-20-000033

examples.py

+18-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from sec_api.index import RenderApi, XbrlApi, ExtractorApi
1+
from sec_api.index import RenderApi, XbrlApi, ExtractorApi, MappingApi
22

33
#
44
# Render API
@@ -12,23 +12,22 @@
1212
)
1313
1414
print(filing_data)
15-
"""
15+
# """
1616

1717
#
1818
# XBRL-to-JSON API example
1919
#
2020
"""
2121
xbrlApi = XbrlApi("YOUR_API_KEY")
2222
23-
2423
# 10-K HTM File URL example
2524
xbrl_json_1 = xbrlApi.xbrl_to_json(
2625
htm_url="https://www.sec.gov/Archives/edgar/data/320193/000032019320000096/aapl-20200926.htm"
2726
)
2827
29-
print(xbrl_json_1["StatementsOfIncome"])
30-
print(xbrl_json_1["BalanceSheets"])
31-
print(xbrl_json_1["StatementsOfCashFlows"])
28+
# print(xbrl_json_1["StatementsOfIncome"])
29+
# print(xbrl_json_1["BalanceSheets"])
30+
# print(xbrl_json_1["StatementsOfCashFlows"])
3231
3332
# 10-K XBRL File URL example
3433
xbrl_json_2 = xbrlApi.xbrl_to_json(
@@ -38,7 +37,7 @@
3837
# 10-K XBRL File URL example
3938
xbrl_json_3 = xbrlApi.xbrl_to_json(accession_no="0001564590-21-004599")
4039
41-
"""
40+
# """
4241

4342
#
4443
# Extractor API Example
@@ -54,4 +53,16 @@
5453
5554
print(section_text)
5655
print(section_html)
56+
# """
57+
58+
59+
#
60+
# Mapping API Example
61+
#
5762
"""
63+
mappingApi = MappingApi("YOUR_API_KEY")
64+
65+
result = mappingApi.resolve("cik", "927355")
66+
67+
print(result)
68+
# """

sec_api/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
from sec_api.index import RenderApi
55
from sec_api.index import XbrlApi
66
from sec_api.index import ExtractorApi
7+
from sec_api.index import MappingApi

sec_api/index.py

+119-10
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import requests
22
import json
33
import re
4+
import time
45

56
query_api_endpoint = "https://api.sec-api.io"
67
full_text_search_api_endpoint = "https://api.sec-api.io/full-text-search"
78
render_api_endpoint = "https://archive.sec-api.io"
89
xbrl_api_endpoint = "https://api.sec-api.io/xbrl-to-json"
910
extractor_api_endpoint = "https://api.sec-api.io/extractor"
11+
mapping_api_endpoint = "https://api.sec-api.io/mapping"
1012

1113

1214
class QueryApi:
@@ -19,8 +21,19 @@ def __init__(self, api_key):
1921
self.api_endpoint = query_api_endpoint + "?token=" + api_key
2022

2123
def get_filings(self, query):
22-
response = requests.post(self.api_endpoint, json=query)
23-
return response.json()
24+
# use backoff strategy to handle "too many requests" error.
25+
for x in range(3):
26+
response = requests.post(self.api_endpoint, json=query)
27+
if response.status_code == 200:
28+
return response.json()
29+
elif response.status_code == 429:
30+
# wait 500 * (x + 1) milliseconds and try again
31+
time.sleep(0.5 * (x + 1))
32+
else:
33+
raise Exception("API error: " + response.status_code)
34+
else:
35+
# request failed
36+
raise Exception("API error")
2437

2538

2639
class FullTextSearchApi:
@@ -33,8 +46,19 @@ def __init__(self, api_key):
3346
self.api_endpoint = full_text_search_api_endpoint + "?token=" + api_key
3447

3548
def get_filings(self, query):
36-
response = requests.post(self.api_endpoint, json=query)
37-
return response.json()
49+
# use backoff strategy to handle "too many requests" error.
50+
for x in range(3):
51+
response = requests.post(self.api_endpoint, json=query)
52+
if response.status_code == 200:
53+
return response.json()
54+
elif response.status_code == 429:
55+
# wait 500 * (x + 1) milliseconds and try again
56+
time.sleep(0.5 * (x + 1))
57+
else:
58+
raise Exception("API error: " + response.status_code)
59+
else:
60+
# request failed
61+
raise Exception("API error")
3862

3963

4064
class RenderApi:
@@ -49,8 +73,20 @@ def __init__(self, api_key):
4973
def get_filing(self, url):
5074
filename = re.sub(r"https://www.sec.gov/Archives/edgar/data", "", url)
5175
_url = self.api_endpoint + filename + "?token=" + self.api_key
52-
response = requests.get(_url)
53-
return response.text
76+
77+
# use backoff strategy to handle "too many requests" error.
78+
for x in range(3):
79+
response = requests.get(_url)
80+
if response.status_code == 200:
81+
return response.text
82+
elif response.status_code == 429:
83+
# wait 500 * (x + 1) milliseconds and try again
84+
time.sleep(0.5 * (x + 1))
85+
else:
86+
raise Exception("API error: " + response.status_code)
87+
else:
88+
# request failed
89+
raise Exception("API error")
5490

5591

5692
class XbrlApi:
@@ -77,8 +113,20 @@ def xbrl_to_json(self, htm_url="", xbrl_url="", accession_no=""):
77113
if len(accession_no):
78114
_url = self.api_endpoint + "&accession-no=" + accession_no
79115

80-
response = requests.get(_url)
81-
return json.loads(response.text)
116+
# use backoff strategy to handle "too many requests" error.
117+
for x in range(3):
118+
response = requests.get(_url)
119+
if response.status_code == 200:
120+
data = json.loads(response.text)
121+
return data
122+
elif response.status_code == 429:
123+
# wait 500 * (x + 1) milliseconds and try again
124+
time.sleep(0.5 * (x + 1))
125+
else:
126+
raise Exception("API error: " + response.status_code)
127+
else:
128+
# request failed
129+
raise Exception("API error")
82130

83131

84132
class ExtractorApi:
@@ -104,5 +152,66 @@ def get_section(self, filing_url="", section="1A", return_type="text"):
104152
+ return_type
105153
)
106154

107-
response = requests.get(_url)
108-
return response.text
155+
# use backoff strategy to handle "too many requests" error.
156+
for x in range(3):
157+
response = requests.get(_url)
158+
if response.status_code == 200:
159+
return response.text
160+
elif response.status_code == 429:
161+
# wait 500 * (x + 1) milliseconds and try again
162+
time.sleep(0.5 * (x + 1))
163+
else:
164+
raise Exception("API error: " + response.status_code)
165+
else:
166+
# request failed
167+
raise Exception("API error")
168+
169+
170+
class MappingApi:
171+
"""
172+
Base class for CUSIP/CIK/Ticker Mapping API
173+
Documentation: https://sec-api.io/docs/mapping-api
174+
175+
cik, ticker, cusip, name, exchange, sector, industry
176+
"""
177+
178+
def __init__(self, api_key):
179+
self.api_key = api_key
180+
self.api_endpoint = mapping_api_endpoint
181+
self.supported_parameters = [
182+
"cik",
183+
"ticker",
184+
"cusip",
185+
"name",
186+
"exchange",
187+
"sector",
188+
"industry",
189+
]
190+
191+
def resolve(self, parameter="", value=""):
192+
if not parameter.lower() in self.supported_parameters:
193+
raise ValueError("Parameter not supported")
194+
195+
_url = (
196+
self.api_endpoint
197+
+ "/"
198+
+ parameter.lower()
199+
+ "/"
200+
+ value
201+
+ "?token="
202+
+ self.api_key
203+
)
204+
205+
# use backoff strategy to handle "too many requests" error.
206+
for x in range(3):
207+
response = requests.get(_url)
208+
if response.status_code == 200:
209+
return response.json()
210+
elif response.status_code == 429:
211+
# wait 500 * (x + 1) milliseconds and try again
212+
time.sleep(0.5 * (x + 1))
213+
else:
214+
raise Exception("API error: " + response.status_code)
215+
else:
216+
# request failed
217+
raise Exception("API error")

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
setup(
77
name="sec-api",
8-
version="1.0.8",
8+
version="1.0.9",
99
author="SEC API",
1010
author_email="[email protected]",
1111
description="SEC EDGAR Filings API",

0 commit comments

Comments
 (0)