Skip to content

Commit b1347b4

Browse files
added Form D API, Form N-PORT API, Form ADV API
1 parent 9f8287d commit b1347b4

5 files changed

Lines changed: 307 additions & 2 deletions

File tree

README.md

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ It includes:
1212
- [Insider Trading Data API](#insider-trading-data-api)
1313
- [13F Institutional Investor Database](#13f-institutional-investor-database)
1414
- [CUSIP/CIK/Ticker Mapping API](#cusipcikticker-mapping-api)
15+
- [Form N-PORT API](#form-n-port-api)
16+
- [Form D API](#form-d-api)
17+
- [Form ADV API](#form-adv-api)
1518

1619

1720
# Data Coverage
@@ -652,6 +655,8 @@ insider_trades = insiderTradingApi.get_data({
652655
print(insider_trades["transactions"])
653656
```
654657

658+
> See the documentation for more details: https://sec-api.io/docs/insider-ownership-trading-api
659+
655660
### Response Example
656661
```json
657662
[
@@ -703,6 +708,102 @@ print(insider_trades["transactions"])
703708
]
704709
```
705710

711+
# Form N-PORT API
712+
713+
Access and find standardized N-PORT SEC filings.
714+
715+
```python
716+
from sec_api import FormNportApi
717+
718+
nportApi = FormNportApi("YOUR_API_KEY")
719+
720+
response = nportApi.get_data(
721+
{
722+
"query": {"query_string": {"query": "fundInfo.totAssets:[100000000 TO *]"}},
723+
"from": "0",
724+
"size": "10",
725+
"sort": [{"filedAt": {"order": "desc"}}],
726+
}
727+
)
728+
729+
print(response["filings"])
730+
```
731+
732+
> See the documentation for more details: https://sec-api.io/docs/n-port-data-api
733+
734+
735+
# Form D API
736+
737+
Search and find Form D offering filings by any filing property, e.g. total offering amount, offerings filed by
738+
hedge funds, type of securities offered and many more.
739+
740+
```python
741+
from sec_api import FormDApi
742+
743+
formDApi = FormDApi("YOUR_API_KEY")
744+
745+
response = formDApi.get_data(
746+
{
747+
"query": {
748+
"query_string": {
749+
"query": "offeringData.offeringSalesAmounts.totalOfferingAmount:[1000000 TO *]"
750+
}
751+
},
752+
"from": "0",
753+
"size": "10",
754+
"sort": [{"filedAt": {"order": "desc"}}],
755+
}
756+
)
757+
758+
print(response["offerings"])
759+
```
760+
761+
> See the documentation for more details: https://sec-api.io/docs/form-d-xml-json-api
762+
763+
764+
# Form ADV API
765+
766+
Search the entire ADV filing database and find all ADV filings filed by firm advisers (SEC and state registered),
767+
individual advisers and firm brochures published in part 2 of ADV filings. The database comprises 41,000 ADV filings
768+
filed by advisory firms and 380,000 individual advisers and is updated daily.
769+
Search and find ADV filings by any filing property, such as CRD, assets under management,
770+
type of adviser (e.g. broker dealer) and more.
771+
772+
```python
773+
from sec_api import FormAdvApi
774+
775+
formAdvApi = FormAdvApi("YOUR_API_KEY")
776+
777+
response = formAdvApi.get_firms(
778+
{
779+
"query": {"query_string": {"query": "Info.FirmCrdNb:361"}},
780+
"from": "0",
781+
"size": "10",
782+
"sort": [{"Info.FirmCrdNb": {"order": "desc"}}],
783+
}
784+
)
785+
786+
print(response["filings"])
787+
788+
response = formAdvApi.get_individuals(
789+
{
790+
"query": {"query_string": {"query": "CrntEmps.CrntEmp.orgPK:149777"}},
791+
"from": "0",
792+
"size": "10",
793+
"sort": [{"id": {"order": "desc"}}],
794+
}
795+
)
796+
797+
print(response["filings"])
798+
799+
response = formAdvApi.get_brochures(149777)
800+
801+
print(response["brochures"])
802+
```
803+
804+
> See the documentation for more details: https://sec-api.io/docs/investment-adviser-and-adv-api
805+
806+
706807
# Query API Response Format
707808

708809
- `accessionNo` (string) - Accession number of filing, e.g. 0000028917-20-000033
@@ -771,7 +872,7 @@ print(insider_trades["transactions"])
771872
- `Shared` (integer) - Shared, e.g. 345
772873
- `None` (integer) - None, e.g. 345
773874

774-
## Example JSON Response
875+
## Query API Example JSON Response
775876

776877
```json
777878
{

examples.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
MappingApi,
66
ExecCompApi,
77
InsiderTradingApi,
8+
FormNportApi,
9+
FormDApi,
10+
FormAdvApi,
811
)
912

1013
#
@@ -121,3 +124,76 @@
121124
122125
print(insider_trades["transactions"])
123126
# """
127+
128+
#
129+
# Form NPORT API Example
130+
#
131+
"""
132+
nportApi = FormNportApi("YOUR_API_KEY")
133+
134+
response = nportApi.get_data(
135+
{
136+
"query": {"query_string": {"query": "fundInfo.totAssets:[100000000 TO *]"}},
137+
"from": "0",
138+
"size": "2",
139+
"sort": [{"filedAt": {"order": "desc"}}],
140+
}
141+
)
142+
143+
print(response["filings"])
144+
# """
145+
146+
#
147+
# Form D API Example
148+
#
149+
"""
150+
formDApi = FormDApi("YOUR_API_KEY")
151+
152+
response = formDApi.get_data(
153+
{
154+
"query": {
155+
"query_string": {
156+
"query": "offeringData.offeringSalesAmounts.totalOfferingAmount:[1000000 TO *]"
157+
}
158+
},
159+
"from": "0",
160+
"size": "10",
161+
"sort": [{"filedAt": {"order": "desc"}}],
162+
}
163+
)
164+
165+
print(response["offerings"])
166+
# """
167+
168+
#
169+
# Form ADV API Example
170+
#
171+
"""
172+
formAdvApi = FormAdvApi("YOUR_API_KEY")
173+
174+
response = formAdvApi.get_firms(
175+
{
176+
"query": {"query_string": {"query": "Info.FirmCrdNb:361"}},
177+
"from": "0",
178+
"size": "10",
179+
"sort": [{"Info.FirmCrdNb": {"order": "desc"}}],
180+
}
181+
)
182+
183+
print(response["filings"])
184+
185+
response = formAdvApi.get_individuals(
186+
{
187+
"query": {"query_string": {"query": "CrntEmps.CrntEmp.orgPK:149777"}},
188+
"from": "0",
189+
"size": "10",
190+
"sort": [{"id": {"order": "desc"}}],
191+
}
192+
)
193+
194+
print(response["filings"])
195+
196+
response = formAdvApi.get_brochures(149777)
197+
198+
print(response["brochures"])
199+
# """

sec_api/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@
77
from sec_api.index import MappingApi
88
from sec_api.index import ExecCompApi
99
from sec_api.index import InsiderTradingApi
10+
from sec_api.index import FormNportApi
11+
from sec_api.index import FormDApi
12+
from sec_api.index import FormAdvApi

sec_api/index.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
mapping_api_endpoint = "https://api.sec-api.io/mapping"
1212
exec_comp_api_endpoint = "https://api.sec-api.io/compensation"
1313
insider_api_endpoint = "https://api.sec-api.io/insider-trading"
14+
form_nport_api_endpoint = "https://api.sec-api.io/form-nport"
15+
form_d_api_endpoint = "https://api.sec-api.io/form-d"
16+
form_adv_endpoint = "https://api.sec-api.io/form-adv"
1417

1518

1619
def handle_api_error(response):
@@ -298,3 +301,120 @@ def get_data(self, query):
298301
handle_api_error(response)
299302
else:
300303
handle_api_error(response)
304+
305+
306+
class FormNportApi:
307+
"""
308+
Base class for Form NPORT API
309+
"""
310+
311+
def __init__(self, api_key):
312+
self.api_key = api_key
313+
self.api_endpoint = form_nport_api_endpoint + "?token=" + api_key
314+
315+
def get_data(self, query):
316+
response = {}
317+
318+
# use backoff strategy to handle "too many requests" error.
319+
for x in range(3):
320+
response = requests.post(self.api_endpoint, json=query)
321+
if response.status_code == 200:
322+
return response.json()
323+
elif response.status_code == 429:
324+
# wait 500 * (x + 1) milliseconds and try again
325+
time.sleep(0.5 * (x + 1))
326+
else:
327+
handle_api_error(response)
328+
else:
329+
handle_api_error(response)
330+
331+
332+
class FormDApi:
333+
"""
334+
Base class for Form D API
335+
"""
336+
337+
def __init__(self, api_key):
338+
self.api_key = api_key
339+
self.api_endpoint = form_d_api_endpoint + "?token=" + api_key
340+
341+
def get_data(self, query):
342+
response = {}
343+
344+
# use backoff strategy to handle "too many requests" error.
345+
for x in range(3):
346+
response = requests.post(self.api_endpoint, json=query)
347+
if response.status_code == 200:
348+
return response.json()
349+
elif response.status_code == 429:
350+
# wait 500 * (x + 1) milliseconds and try again
351+
time.sleep(0.5 * (x + 1))
352+
else:
353+
handle_api_error(response)
354+
else:
355+
handle_api_error(response)
356+
357+
358+
class FormAdvApi:
359+
"""
360+
Base class for Form ADV API
361+
"""
362+
363+
def __init__(self, api_key):
364+
self.api_key = api_key
365+
self.api_endpoint_firm = form_adv_endpoint + "/firm" + "?token=" + api_key
366+
self.api_endpoint_individual = (
367+
form_adv_endpoint + "/individual" + "?token=" + api_key
368+
)
369+
self.api_endpoint_brochures = form_adv_endpoint + "/brochures?token=" + api_key
370+
371+
def get_firms(self, query):
372+
response = {}
373+
374+
# use backoff strategy to handle "too many requests" error.
375+
for x in range(3):
376+
response = requests.post(self.api_endpoint_firm, json=query)
377+
if response.status_code == 200:
378+
return response.json()
379+
elif response.status_code == 429:
380+
# wait 500 * (x + 1) milliseconds and try again
381+
time.sleep(0.5 * (x + 1))
382+
else:
383+
handle_api_error(response)
384+
else:
385+
handle_api_error(response)
386+
387+
def get_individuals(self, query):
388+
response = {}
389+
390+
# use backoff strategy to handle "too many requests" error.
391+
for x in range(3):
392+
response = requests.post(self.api_endpoint_individual, json=query)
393+
if response.status_code == 200:
394+
return response.json()
395+
elif response.status_code == 429:
396+
# wait 500 * (x + 1) milliseconds and try again
397+
time.sleep(0.5 * (x + 1))
398+
else:
399+
handle_api_error(response)
400+
else:
401+
handle_api_error(response)
402+
403+
def get_brochures(self, crd):
404+
endpoint = (
405+
form_adv_endpoint + "/brochures/" + str(crd) + "?token=" + self.api_key
406+
)
407+
response = {}
408+
409+
# use backoff strategy to handle "too many requests" error.
410+
for x in range(3):
411+
response = requests.get(endpoint)
412+
if response.status_code == 200:
413+
return response.json()
414+
elif response.status_code == 429:
415+
# wait 500 * (x + 1) milliseconds and try again
416+
time.sleep(0.5 * (x + 1))
417+
else:
418+
handle_api_error(response)
419+
else:
420+
handle_api_error(response)

setup.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
setup(
77
name="sec-api",
8-
version="1.0.13",
8+
version="1.0.14",
99
author="SEC API",
1010
author_email="support@sec-api.io",
1111
description="SEC EDGAR Filings API",
@@ -32,6 +32,7 @@
3232
keywords=[
3333
"SEC EDGAR API",
3434
"SEC Filings API",
35+
"SEC Filing Search API",
3536
"SEC Full-Text Search API",
3637
"EDGAR API",
3738
"Finance",
@@ -48,6 +49,10 @@
4849
"Financial Statements API",
4950
"Insider Trading Data",
5051
"Executive Compensation Data",
52+
"Form D Offerings API",
53+
"Form N-PORT API",
54+
"Form ADV API",
55+
"10-K/10-Q/8-K Section Extractor API",
5156
],
5257
project_urls={
5358
"Bug Reports": "https://github.com/janlukasschroeder/sec-api-python/issues",

0 commit comments

Comments
 (0)