secfeed 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
secfeed-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,32 @@
1
+ Metadata-Version: 2.1
2
+ Name: secfeed
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the SecFeed SEC EDGAR API
5
+ License: MIT
6
+ Project-URL: Homepage, https://secfeedapi.com
7
+ Project-URL: Documentation, https://secfeedapi.com/docs/
8
+ Keywords: sec,edgar,filings,xbrl,13f,insider trading,api
9
+ Requires-Python: >=3.8
10
+ Description-Content-Type: text/markdown
11
+
12
+ # secfeed (Python)
13
+
14
+ Client for the [SecFeed](https://secfeedapi.com) SEC EDGAR API. No dependencies, Python 3.8+.
15
+
16
+ ```bash
17
+ pip install secfeed
18
+ ```
19
+
20
+ ```python
21
+ from secfeed import Client
22
+
23
+ sf = Client("sf_...") # free key at https://secfeedapi.com
24
+ sf.filings('ticker:AAPL AND formType:"10-K"', size=5)
25
+ sf.insider_trades(ticker="NVDA", code="P")
26
+ sf.financial_statements("AAPL")
27
+ sf.events_8k(item="5.02")
28
+ for filing in sf.stream(form_types=["8-K"]):
29
+ print(filing["companyName"], filing["formType"])
30
+ ```
31
+
32
+ Every endpoint in the [API docs](https://secfeedapi.com/docs/) has a method. Errors raise `SecFeedError`.
@@ -0,0 +1,21 @@
1
+ # secfeed (Python)
2
+
3
+ Client for the [SecFeed](https://secfeedapi.com) SEC EDGAR API. No dependencies, Python 3.8+.
4
+
5
+ ```bash
6
+ pip install secfeed
7
+ ```
8
+
9
+ ```python
10
+ from secfeed import Client
11
+
12
+ sf = Client("sf_...") # free key at https://secfeedapi.com
13
+ sf.filings('ticker:AAPL AND formType:"10-K"', size=5)
14
+ sf.insider_trades(ticker="NVDA", code="P")
15
+ sf.financial_statements("AAPL")
16
+ sf.events_8k(item="5.02")
17
+ for filing in sf.stream(form_types=["8-K"]):
18
+ print(filing["companyName"], filing["formType"])
19
+ ```
20
+
21
+ Every endpoint in the [API docs](https://secfeedapi.com/docs/) has a method. Errors raise `SecFeedError`.
@@ -0,0 +1,19 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "secfeed"
7
+ version = "0.1.0"
8
+ description = "Python SDK for the SecFeed SEC EDGAR API"
9
+ requires-python = ">=3.8"
10
+ readme = "README.md"
11
+ license = {text = "MIT"}
12
+ keywords = ["sec", "edgar", "filings", "xbrl", "13f", "insider trading", "api"]
13
+
14
+ [project.urls]
15
+ Homepage = "https://secfeedapi.com"
16
+ Documentation = "https://secfeedapi.com/docs/"
17
+
18
+ [tool.setuptools]
19
+ packages = ["secfeed"]
@@ -0,0 +1,150 @@
1
+ """SecFeed Python SDK. Stdlib only.
2
+
3
+ pip install secfeed
4
+
5
+ from secfeed import Client
6
+ sf = Client("sf_...")
7
+ sf.filings('ticker:AAPL AND formType:"10-K"')
8
+ sf.insider_trades(ticker="NVDA", code="P")
9
+ for filing in sf.stream(form_types=["8-K"]): ...
10
+ """
11
+ import json
12
+ import urllib.parse
13
+ import urllib.request
14
+
15
+ __all__ = ["Client", "SecFeedError"]
16
+
17
+
18
+ class SecFeedError(Exception):
19
+ pass
20
+
21
+
22
+ class Client:
23
+ def __init__(self, api_key, base_url="https://secfeedapi.com"):
24
+ self.key, self.base = api_key, base_url.rstrip("/")
25
+
26
+ def _req(self, method, path, params=None, body=None, raw=False):
27
+ q = urllib.parse.urlencode({k: v for k, v in (params or {}).items() if v is not None})
28
+ req = urllib.request.Request(f"{self.base}{path}{'?' + q if q else ''}", method=method,
29
+ data=json.dumps(body).encode() if body is not None else None,
30
+ headers={"Authorization": f"Bearer {self.key}", "Content-Type": "application/json"})
31
+ try:
32
+ with urllib.request.urlopen(req, timeout=120) as r:
33
+ data = r.read()
34
+ except urllib.error.HTTPError as e:
35
+ raise SecFeedError(f"{e.code}: {e.read().decode(errors='replace')}") from None
36
+ return data if raw else json.loads(data)
37
+
38
+ # search
39
+ def filings(self, query, start=0, size=50, sort="desc"):
40
+ return self._req("POST", "/v1/filings", body={"query": query, "from": start, "size": size, "sort": sort})
41
+
42
+ def full_text_search(self, query, form_types=None, start_date=None, end_date=None, page=1):
43
+ return self._req("GET", "/v1/full-text-search", {"query": query, "formTypes": ",".join(form_types or []) or None,
44
+ "startDate": start_date, "endDate": end_date, "page": page})
45
+
46
+ # documents
47
+ def files(self, cik, accession_no):
48
+ return self._req("GET", "/v1/files", {"cik": cik, "accessionNo": accession_no})
49
+
50
+ def download(self, url):
51
+ return self._req("GET", "/v1/download", {"url": url}, raw=True)
52
+
53
+ def pdf(self, url):
54
+ return self._req("GET", "/v1/pdf", {"url": url}, raw=True)
55
+
56
+ def extract(self, url, item, type="text"):
57
+ return self._req("GET", "/v1/extractor", {"url": url, "item": item, "type": type}, raw=True).decode()
58
+
59
+ # financials
60
+ def xbrl_to_json(self, accession_no=None, htm_url=None, cik=None):
61
+ return self._req("GET", "/v1/xbrl-to-json", {"accessionNo": accession_no, "htmUrl": htm_url, "cik": cik})
62
+
63
+ def financial_statements(self, ticker, form_type="10-K", n=1):
64
+ return self._req("GET", "/v1/financial-statements", {"ticker": ticker, "formType": form_type, "n": n})
65
+
66
+ def company_facts(self, ticker):
67
+ return self._req("GET", "/v1/xbrl/company-facts", {"ticker": ticker})
68
+
69
+ def frames(self, concept, period, unit="USD"):
70
+ return self._req("GET", "/v1/xbrl/frames", {"concept": concept, "period": period, "unit": unit})
71
+
72
+ # ownership, events, forms
73
+ def insider_trades(self, **filters):
74
+ return self._req("GET", "/v1/insider-trading", filters)
75
+
76
+ def holdings_13f(self, cusip=None, cik=None, period=None):
77
+ return self._req("GET", "/v1/form-13f/holdings", {"cusip": cusip, "cik": cik, "period": period})
78
+
79
+ def form(self, family, **filters):
80
+ """family: form-144, form-13f, form-13d-13g, form-d, form-c, reg-a, n-port, n-cen, n-px"""
81
+ return self._req("GET", f"/v1/{family}", filters)
82
+
83
+ def events_8k(self, item=None, **filters):
84
+ return self._req("GET", "/v1/form-8k", {"item": item, **filters})
85
+
86
+ # company data & reference
87
+ def entity(self, ticker_or_cik):
88
+ return self._req("GET", "/v1/entities", {"cik": ticker_or_cik})
89
+
90
+ def map_ticker(self, ticker):
91
+ return self._req("GET", "/v1/mapping/ticker", {"ticker": ticker})
92
+
93
+ def map_cusip(self, cusip):
94
+ return self._req("GET", "/v1/mapping/cusip", {"cusip": cusip})
95
+
96
+ def shares_outstanding(self, ticker):
97
+ return self._req("GET", "/v1/shares-outstanding", {"ticker": ticker})
98
+
99
+ def auditor(self, ticker):
100
+ return self._req("GET", "/v1/auditor", {"ticker": ticker})
101
+
102
+ def executive_compensation(self, ticker):
103
+ return self._req("GET", "/v1/executive-compensation", {"ticker": ticker})
104
+
105
+ def directors(self, ticker):
106
+ return self._req("GET", "/v1/directors", {"ticker": ticker})
107
+
108
+ def subsidiaries(self, ticker):
109
+ return self._req("GET", "/v1/subsidiaries", {"ticker": ticker})
110
+
111
+ # datasets extracted from proxy statements, audit reports and enforcement releases
112
+ # filters: cik, ticker, accessionNo, startDate, endDate, from, size, and data.<path>=value
113
+ def dataset(self, path, **filters):
114
+ return self._req("GET", f"/v1/{path}", filters)
115
+
116
+ def audit_report_content(self, id):
117
+ return self._req("GET", "/v1/audit-reports/content", {"id": id}, raw=True).decode()
118
+
119
+ def ipos(self, **filters):
120
+ return self._req("GET", "/v1/ipo", filters)
121
+
122
+ # enforcement
123
+ def enforcement(self, kind="litigation-releases", page=0, details=False):
124
+ return self._req("GET", f"/v1/enforcement/{kind}", {"page": page, "details": str(details).lower()})
125
+
126
+ # real time
127
+ def stream(self, form_types=None, ciks=None):
128
+ """Yield each new filing as a dict, as soon as EDGAR publishes it."""
129
+ q = urllib.parse.urlencode({"formTypes": ",".join(form_types or []), "ciks": ",".join(map(str, ciks or []))})
130
+ req = urllib.request.Request(f"{self.base}/v1/stream?{q}", headers={"Authorization": f"Bearer {self.key}"})
131
+ with urllib.request.urlopen(req) as r:
132
+ for line in r:
133
+ if line.startswith(b"data: "):
134
+ yield json.loads(line[6:])
135
+
136
+ def add_webhook(self, url, form_types=None, ciks=None):
137
+ return self._req("POST", "/v1/webhooks", body={"url": url, "formTypes": form_types or [], "ciks": ciks or []})
138
+
139
+
140
+ DATASETS = {
141
+ "director_compensation": "director-compensation", "executive_compensation_governance": "executive-compensation-governance",
142
+ "severance_change_in_control": "severance-change-in-control", "board_oversight": "board-oversight",
143
+ "shareholder_rights": "shareholder-rights", "esg_oversight": "esg-oversight", "compliance_signals": "compliance-signals",
144
+ "vote_standards": "vote-standards", "shareholder_proposals_proxy_contests": "shareholder-proposals-proxy-contests",
145
+ "insider_5pct_holder_ownership": "insider-5pct-holder-ownership", "fund_reorganizations": "fund-reorganizations",
146
+ "audit_reports_financial_statements": "audit-reports/financial-statements", "audit_reports_icfr": "audit-reports/icfr",
147
+ "enforcement_actions": "sec-enforcement-actions",
148
+ }
149
+ for _name, _path in DATASETS.items(): # sf.board_oversight(ticker="AAPL") and so on
150
+ setattr(Client, _name, lambda self, _p=_path, **filters: self.dataset(_p, **filters))
@@ -0,0 +1,32 @@
1
+ Metadata-Version: 2.1
2
+ Name: secfeed
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the SecFeed SEC EDGAR API
5
+ License: MIT
6
+ Project-URL: Homepage, https://secfeedapi.com
7
+ Project-URL: Documentation, https://secfeedapi.com/docs/
8
+ Keywords: sec,edgar,filings,xbrl,13f,insider trading,api
9
+ Requires-Python: >=3.8
10
+ Description-Content-Type: text/markdown
11
+
12
+ # secfeed (Python)
13
+
14
+ Client for the [SecFeed](https://secfeedapi.com) SEC EDGAR API. No dependencies, Python 3.8+.
15
+
16
+ ```bash
17
+ pip install secfeed
18
+ ```
19
+
20
+ ```python
21
+ from secfeed import Client
22
+
23
+ sf = Client("sf_...") # free key at https://secfeedapi.com
24
+ sf.filings('ticker:AAPL AND formType:"10-K"', size=5)
25
+ sf.insider_trades(ticker="NVDA", code="P")
26
+ sf.financial_statements("AAPL")
27
+ sf.events_8k(item="5.02")
28
+ for filing in sf.stream(form_types=["8-K"]):
29
+ print(filing["companyName"], filing["formType"])
30
+ ```
31
+
32
+ Every endpoint in the [API docs](https://secfeedapi.com/docs/) has a method. Errors raise `SecFeedError`.
@@ -0,0 +1,7 @@
1
+ README.md
2
+ pyproject.toml
3
+ secfeed/__init__.py
4
+ secfeed.egg-info/PKG-INFO
5
+ secfeed.egg-info/SOURCES.txt
6
+ secfeed.egg-info/dependency_links.txt
7
+ secfeed.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ secfeed
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+