form4api 0.3.1__tar.gz → 0.4.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.
- form4api-0.4.0/PKG-INFO +208 -0
- form4api-0.4.0/README.md +190 -0
- {form4api-0.3.1 → form4api-0.4.0}/form4api/resources/_transactions.py +47 -1
- form4api-0.4.0/form4api.egg-info/PKG-INFO +208 -0
- {form4api-0.3.1 → form4api-0.4.0}/pyproject.toml +27 -27
- {form4api-0.3.1 → form4api-0.4.0}/tests/test_client.py +29 -0
- form4api-0.3.1/PKG-INFO +0 -104
- form4api-0.3.1/README.md +0 -86
- form4api-0.3.1/form4api.egg-info/PKG-INFO +0 -104
- {form4api-0.3.1 → form4api-0.4.0}/LICENSE +0 -0
- {form4api-0.3.1 → form4api-0.4.0}/form4api/__init__.py +0 -0
- {form4api-0.3.1 → form4api-0.4.0}/form4api/_client.py +0 -0
- {form4api-0.3.1 → form4api-0.4.0}/form4api/_errors.py +0 -0
- {form4api-0.3.1 → form4api-0.4.0}/form4api/_types.py +0 -0
- {form4api-0.3.1 → form4api-0.4.0}/form4api/_webhook_utils.py +0 -0
- {form4api-0.3.1 → form4api-0.4.0}/form4api/resources/__init__.py +0 -0
- {form4api-0.3.1 → form4api-0.4.0}/form4api/resources/_companies.py +0 -0
- {form4api-0.3.1 → form4api-0.4.0}/form4api/resources/_insiders.py +0 -0
- {form4api-0.3.1 → form4api-0.4.0}/form4api/resources/_signals.py +0 -0
- {form4api-0.3.1 → form4api-0.4.0}/form4api/resources/_webhooks.py +0 -0
- {form4api-0.3.1 → form4api-0.4.0}/form4api.egg-info/SOURCES.txt +0 -0
- {form4api-0.3.1 → form4api-0.4.0}/form4api.egg-info/dependency_links.txt +0 -0
- {form4api-0.3.1 → form4api-0.4.0}/form4api.egg-info/requires.txt +0 -0
- {form4api-0.3.1 → form4api-0.4.0}/form4api.egg-info/top_level.txt +0 -0
- {form4api-0.3.1 → form4api-0.4.0}/setup.cfg +0 -0
form4api-0.4.0/PKG-INFO
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: form4api
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Python client for the Form4API — real-time SEC Form 4 insider trading data
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://form4api.com
|
|
7
|
+
Project-URL: Documentation, https://form4api.com/docs
|
|
8
|
+
Project-URL: Repository, https://github.com/theodor90/form4api-py.git
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: httpx>=0.27
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-httpx>=0.30; extra == "dev"
|
|
16
|
+
Requires-Dist: respx>=0.21; extra == "dev"
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# form4api
|
|
20
|
+
|
|
21
|
+
Python client for [Form4API](https://form4api.com) — real-time SEC Form 4 insider trading data.
|
|
22
|
+
|
|
23
|
+
Supports Python 3.11+. Uses `httpx` for both sync and async HTTP.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install form4api
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Sync quickstart
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from form4api import Form4ApiClient
|
|
35
|
+
|
|
36
|
+
client = Form4ApiClient("YOUR_API_KEY")
|
|
37
|
+
|
|
38
|
+
# Recent open-market purchases at Apple (excluding 10b5-1 plan trades)
|
|
39
|
+
txns = client.transactions.list(ticker="AAPL", code="P", exclude_10b5=True, per_page=5)
|
|
40
|
+
for t in txns:
|
|
41
|
+
print(t.insider_name, t.insider_title, t.shares_amount, "@", t.price_per_share)
|
|
42
|
+
print(f" open market: {t.is_open_market}, 10b5 plan: {t.is10b5_plan}, value: ${t.total_value:,.0f}")
|
|
43
|
+
|
|
44
|
+
# Company overview (includes SIC, state, website)
|
|
45
|
+
company = client.companies.get("MSFT")
|
|
46
|
+
print(company.name, company.active_insiders, "active insiders")
|
|
47
|
+
print(company.sic_description, company.state_of_incorporation)
|
|
48
|
+
|
|
49
|
+
# Insider detail
|
|
50
|
+
insider = client.insiders.get("0001234567")
|
|
51
|
+
print(insider.name, insider.officer_title)
|
|
52
|
+
|
|
53
|
+
# Cluster-buy signals (Business plan)
|
|
54
|
+
signals = client.signals.list(cluster_buy=True)
|
|
55
|
+
for sig in signals:
|
|
56
|
+
print(sig.company_name, sig.insider_count, "buyers on", sig.signal_date)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Async quickstart
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import asyncio
|
|
63
|
+
from form4api import AsyncForm4ApiClient
|
|
64
|
+
|
|
65
|
+
async def main():
|
|
66
|
+
async with AsyncForm4ApiClient("YOUR_API_KEY") as client:
|
|
67
|
+
txns = await client.transactions.list(ticker="AAPL", per_page=5)
|
|
68
|
+
for t in txns:
|
|
69
|
+
print(t.insider_name, t.shares_amount, "@", t.price_per_share)
|
|
70
|
+
|
|
71
|
+
asyncio.run(main())
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Resources
|
|
75
|
+
|
|
76
|
+
| Resource | Methods |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `client.transactions` | `.list(**params)`, `.paginate(**params)` |
|
|
79
|
+
| `client.insiders` | `.search(name, **params)`, `.get(cik)`, `.transactions(cik, **params)` |
|
|
80
|
+
| `client.companies` | `.get(ticker)`, `.insiders(ticker)` |
|
|
81
|
+
| `client.signals` | `.list(**params)`, `.paginate(**params)` — Business plan |
|
|
82
|
+
| `client.webhooks` | `.create(url, event_types)`, `.list()`, `.delete(id)`, `.events(**params)` |
|
|
83
|
+
|
|
84
|
+
### Not yet in this SDK
|
|
85
|
+
|
|
86
|
+
The API surface is broader than the typed client. These backend features are **available via the REST API and the `form4api-mcp` server today, but don't have a typed SDK resource yet**:
|
|
87
|
+
|
|
88
|
+
- **Form 144** notice-of-proposed-sale — `GET /v1/form144` *(Business)*
|
|
89
|
+
- **Institutional holdings (13F-HR)** — `GET /v1/holdings`, **managers** — `GET /v1/managers` *(Business)*
|
|
90
|
+
- **Sentiment** (MSPR-style, 10b5-1-clean) — `GET /v1/signals/sentiment/{ticker}` *(Business)*
|
|
91
|
+
- **Insider career summary** — `GET /v1/insiders/{cik}/summary` *(Pro)*
|
|
92
|
+
- **Post-trade returns** (1d/1w/1m/3m/6m) + `min_return_*` screening filters on `/v1/transactions` *(visible free; screening Pro)*
|
|
93
|
+
|
|
94
|
+
Until they land in the SDK, call them directly (`client._get("/v1/holdings", {...})`) or see the [full REST reference](https://form4api.com/docs). For LLM workflows, `form4api-mcp` exposes all of the above as tools.
|
|
95
|
+
|
|
96
|
+
### Transaction filters
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
client.transactions.list(
|
|
100
|
+
ticker="AAPL", # filter by ticker
|
|
101
|
+
cik="0000320193", # or by company CIK
|
|
102
|
+
insider_cik="...", # filter by insider CIK
|
|
103
|
+
code="P", # transaction code: P=purchase, S=sale, A=grant, etc.
|
|
104
|
+
from_date="2026-01-01",
|
|
105
|
+
to_date="2026-12-31",
|
|
106
|
+
exclude_10b5=True, # omit trades filed under a Rule 10b5-1 plan
|
|
107
|
+
per_page=100,
|
|
108
|
+
page=1,
|
|
109
|
+
)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Granular filtering (v0.4.0+)
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
# The "just show me real buys & sells" preset: open-market only,
|
|
116
|
+
# no 10b5-1 plan trades, no derivatives.
|
|
117
|
+
client.transactions.list(ticker="AAPL", significant=True)
|
|
118
|
+
|
|
119
|
+
# Multi-code include / exclude (comma-separated SEC codes)
|
|
120
|
+
client.transactions.list(codes="P,S")
|
|
121
|
+
client.transactions.list(exclude_codes="A,M,F,G")
|
|
122
|
+
|
|
123
|
+
# Whole-category filters: open_market | grants | derivatives | gifts | other
|
|
124
|
+
client.transactions.list(category="open_market")
|
|
125
|
+
client.transactions.list(exclude_category="derivatives")
|
|
126
|
+
client.transactions.list(exclude_derivative=True)
|
|
127
|
+
|
|
128
|
+
# Trade-size screening (Pro plan or higher)
|
|
129
|
+
client.transactions.list(min_value=1_000_000) # USD, shares x price
|
|
130
|
+
client.transactions.list(min_shares=10_000, max_shares=100_000)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Transaction fields
|
|
134
|
+
|
|
135
|
+
| Field | Type | Description |
|
|
136
|
+
|-------|------|-------------|
|
|
137
|
+
| `ticker` | `str` | Stock ticker |
|
|
138
|
+
| `company_name` | `str` | Company name |
|
|
139
|
+
| `insider_name` | `str` | Insider full name |
|
|
140
|
+
| `insider_cik` | `str` | Insider CIK |
|
|
141
|
+
| `insider_title` | `str \| None` | Officer title as reported on the Form 4 |
|
|
142
|
+
| `is_director` | `bool` | Director flag |
|
|
143
|
+
| `is_officer` | `bool` | Officer flag |
|
|
144
|
+
| `is10_pct_owner` | `bool` | 10% owner flag |
|
|
145
|
+
| `accession_number` | `str` | SEC accession number |
|
|
146
|
+
| `security_title` | `str` | Security type |
|
|
147
|
+
| `transaction_code` | `str` | Transaction code |
|
|
148
|
+
| `is_open_market` | `bool` | `True` when code is P or S (not grants/awards) |
|
|
149
|
+
| `is10b5_plan` | `bool` | Filed under a Rule 10b5-1 pre-scheduled trading plan |
|
|
150
|
+
| `shares_amount` | `float` | Shares transacted |
|
|
151
|
+
| `price_per_share` | `float \| None` | Price per share |
|
|
152
|
+
| `total_value` | `float \| None` | `shares_amount × price_per_share` in USD |
|
|
153
|
+
| `shares_owned_after` | `float \| None` | Holdings after transaction |
|
|
154
|
+
| `direct_indirect` | `str \| None` | "D" (direct) or "I" (indirect) |
|
|
155
|
+
| `is_derivative` | `bool` | Derivative security flag |
|
|
156
|
+
| `transaction_date` | `str` | ISO datetime |
|
|
157
|
+
| `period_of_report` | `str` | ISO datetime |
|
|
158
|
+
|
|
159
|
+
### Company fields
|
|
160
|
+
|
|
161
|
+
| Field | Type | Description |
|
|
162
|
+
|-------|------|-------------|
|
|
163
|
+
| `cik` | `str` | SEC CIK |
|
|
164
|
+
| `name` | `str` | Company name |
|
|
165
|
+
| `ticker` | `str \| None` | Stock ticker |
|
|
166
|
+
| `exchange` | `str \| None` | Exchange |
|
|
167
|
+
| `total_filings` | `int` | Total Form 4 filings |
|
|
168
|
+
| `active_insiders` | `int` | Distinct insiders who have filed |
|
|
169
|
+
| `sic_description` | `str \| None` | SEC SIC industry description |
|
|
170
|
+
| `state_of_incorporation` | `str \| None` | Two-letter state code |
|
|
171
|
+
| `website` | `str \| None` | Company website as filed with SEC |
|
|
172
|
+
|
|
173
|
+
### Pagination
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
# transactions.paginate() — yields one list per page automatically
|
|
177
|
+
all_txns = []
|
|
178
|
+
for batch in client.transactions.paginate(ticker="NVDA", exclude_10b5=True, per_page=500):
|
|
179
|
+
all_txns.extend(batch)
|
|
180
|
+
|
|
181
|
+
# signals.paginate()
|
|
182
|
+
all_signals = []
|
|
183
|
+
for batch in client.signals.paginate(cluster_buy=True, per_page=100):
|
|
184
|
+
all_signals.extend(batch)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Error handling
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from form4api import Form4ApiClient, AuthError, PlanError, RateLimitError, NotFoundError
|
|
191
|
+
|
|
192
|
+
client = Form4ApiClient("YOUR_API_KEY")
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
signals = client.signals.list()
|
|
196
|
+
except PlanError as e:
|
|
197
|
+
print(f"Upgrade required")
|
|
198
|
+
except RateLimitError as e:
|
|
199
|
+
print(f"Retry after {e.retry_after}s")
|
|
200
|
+
except AuthError:
|
|
201
|
+
print("Invalid API key")
|
|
202
|
+
except NotFoundError:
|
|
203
|
+
print("Resource not found")
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT
|
form4api-0.4.0/README.md
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# form4api
|
|
2
|
+
|
|
3
|
+
Python client for [Form4API](https://form4api.com) — real-time SEC Form 4 insider trading data.
|
|
4
|
+
|
|
5
|
+
Supports Python 3.11+. Uses `httpx` for both sync and async HTTP.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install form4api
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Sync quickstart
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from form4api import Form4ApiClient
|
|
17
|
+
|
|
18
|
+
client = Form4ApiClient("YOUR_API_KEY")
|
|
19
|
+
|
|
20
|
+
# Recent open-market purchases at Apple (excluding 10b5-1 plan trades)
|
|
21
|
+
txns = client.transactions.list(ticker="AAPL", code="P", exclude_10b5=True, per_page=5)
|
|
22
|
+
for t in txns:
|
|
23
|
+
print(t.insider_name, t.insider_title, t.shares_amount, "@", t.price_per_share)
|
|
24
|
+
print(f" open market: {t.is_open_market}, 10b5 plan: {t.is10b5_plan}, value: ${t.total_value:,.0f}")
|
|
25
|
+
|
|
26
|
+
# Company overview (includes SIC, state, website)
|
|
27
|
+
company = client.companies.get("MSFT")
|
|
28
|
+
print(company.name, company.active_insiders, "active insiders")
|
|
29
|
+
print(company.sic_description, company.state_of_incorporation)
|
|
30
|
+
|
|
31
|
+
# Insider detail
|
|
32
|
+
insider = client.insiders.get("0001234567")
|
|
33
|
+
print(insider.name, insider.officer_title)
|
|
34
|
+
|
|
35
|
+
# Cluster-buy signals (Business plan)
|
|
36
|
+
signals = client.signals.list(cluster_buy=True)
|
|
37
|
+
for sig in signals:
|
|
38
|
+
print(sig.company_name, sig.insider_count, "buyers on", sig.signal_date)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Async quickstart
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
import asyncio
|
|
45
|
+
from form4api import AsyncForm4ApiClient
|
|
46
|
+
|
|
47
|
+
async def main():
|
|
48
|
+
async with AsyncForm4ApiClient("YOUR_API_KEY") as client:
|
|
49
|
+
txns = await client.transactions.list(ticker="AAPL", per_page=5)
|
|
50
|
+
for t in txns:
|
|
51
|
+
print(t.insider_name, t.shares_amount, "@", t.price_per_share)
|
|
52
|
+
|
|
53
|
+
asyncio.run(main())
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Resources
|
|
57
|
+
|
|
58
|
+
| Resource | Methods |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `client.transactions` | `.list(**params)`, `.paginate(**params)` |
|
|
61
|
+
| `client.insiders` | `.search(name, **params)`, `.get(cik)`, `.transactions(cik, **params)` |
|
|
62
|
+
| `client.companies` | `.get(ticker)`, `.insiders(ticker)` |
|
|
63
|
+
| `client.signals` | `.list(**params)`, `.paginate(**params)` — Business plan |
|
|
64
|
+
| `client.webhooks` | `.create(url, event_types)`, `.list()`, `.delete(id)`, `.events(**params)` |
|
|
65
|
+
|
|
66
|
+
### Not yet in this SDK
|
|
67
|
+
|
|
68
|
+
The API surface is broader than the typed client. These backend features are **available via the REST API and the `form4api-mcp` server today, but don't have a typed SDK resource yet**:
|
|
69
|
+
|
|
70
|
+
- **Form 144** notice-of-proposed-sale — `GET /v1/form144` *(Business)*
|
|
71
|
+
- **Institutional holdings (13F-HR)** — `GET /v1/holdings`, **managers** — `GET /v1/managers` *(Business)*
|
|
72
|
+
- **Sentiment** (MSPR-style, 10b5-1-clean) — `GET /v1/signals/sentiment/{ticker}` *(Business)*
|
|
73
|
+
- **Insider career summary** — `GET /v1/insiders/{cik}/summary` *(Pro)*
|
|
74
|
+
- **Post-trade returns** (1d/1w/1m/3m/6m) + `min_return_*` screening filters on `/v1/transactions` *(visible free; screening Pro)*
|
|
75
|
+
|
|
76
|
+
Until they land in the SDK, call them directly (`client._get("/v1/holdings", {...})`) or see the [full REST reference](https://form4api.com/docs). For LLM workflows, `form4api-mcp` exposes all of the above as tools.
|
|
77
|
+
|
|
78
|
+
### Transaction filters
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
client.transactions.list(
|
|
82
|
+
ticker="AAPL", # filter by ticker
|
|
83
|
+
cik="0000320193", # or by company CIK
|
|
84
|
+
insider_cik="...", # filter by insider CIK
|
|
85
|
+
code="P", # transaction code: P=purchase, S=sale, A=grant, etc.
|
|
86
|
+
from_date="2026-01-01",
|
|
87
|
+
to_date="2026-12-31",
|
|
88
|
+
exclude_10b5=True, # omit trades filed under a Rule 10b5-1 plan
|
|
89
|
+
per_page=100,
|
|
90
|
+
page=1,
|
|
91
|
+
)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Granular filtering (v0.4.0+)
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
# The "just show me real buys & sells" preset: open-market only,
|
|
98
|
+
# no 10b5-1 plan trades, no derivatives.
|
|
99
|
+
client.transactions.list(ticker="AAPL", significant=True)
|
|
100
|
+
|
|
101
|
+
# Multi-code include / exclude (comma-separated SEC codes)
|
|
102
|
+
client.transactions.list(codes="P,S")
|
|
103
|
+
client.transactions.list(exclude_codes="A,M,F,G")
|
|
104
|
+
|
|
105
|
+
# Whole-category filters: open_market | grants | derivatives | gifts | other
|
|
106
|
+
client.transactions.list(category="open_market")
|
|
107
|
+
client.transactions.list(exclude_category="derivatives")
|
|
108
|
+
client.transactions.list(exclude_derivative=True)
|
|
109
|
+
|
|
110
|
+
# Trade-size screening (Pro plan or higher)
|
|
111
|
+
client.transactions.list(min_value=1_000_000) # USD, shares x price
|
|
112
|
+
client.transactions.list(min_shares=10_000, max_shares=100_000)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Transaction fields
|
|
116
|
+
|
|
117
|
+
| Field | Type | Description |
|
|
118
|
+
|-------|------|-------------|
|
|
119
|
+
| `ticker` | `str` | Stock ticker |
|
|
120
|
+
| `company_name` | `str` | Company name |
|
|
121
|
+
| `insider_name` | `str` | Insider full name |
|
|
122
|
+
| `insider_cik` | `str` | Insider CIK |
|
|
123
|
+
| `insider_title` | `str \| None` | Officer title as reported on the Form 4 |
|
|
124
|
+
| `is_director` | `bool` | Director flag |
|
|
125
|
+
| `is_officer` | `bool` | Officer flag |
|
|
126
|
+
| `is10_pct_owner` | `bool` | 10% owner flag |
|
|
127
|
+
| `accession_number` | `str` | SEC accession number |
|
|
128
|
+
| `security_title` | `str` | Security type |
|
|
129
|
+
| `transaction_code` | `str` | Transaction code |
|
|
130
|
+
| `is_open_market` | `bool` | `True` when code is P or S (not grants/awards) |
|
|
131
|
+
| `is10b5_plan` | `bool` | Filed under a Rule 10b5-1 pre-scheduled trading plan |
|
|
132
|
+
| `shares_amount` | `float` | Shares transacted |
|
|
133
|
+
| `price_per_share` | `float \| None` | Price per share |
|
|
134
|
+
| `total_value` | `float \| None` | `shares_amount × price_per_share` in USD |
|
|
135
|
+
| `shares_owned_after` | `float \| None` | Holdings after transaction |
|
|
136
|
+
| `direct_indirect` | `str \| None` | "D" (direct) or "I" (indirect) |
|
|
137
|
+
| `is_derivative` | `bool` | Derivative security flag |
|
|
138
|
+
| `transaction_date` | `str` | ISO datetime |
|
|
139
|
+
| `period_of_report` | `str` | ISO datetime |
|
|
140
|
+
|
|
141
|
+
### Company fields
|
|
142
|
+
|
|
143
|
+
| Field | Type | Description |
|
|
144
|
+
|-------|------|-------------|
|
|
145
|
+
| `cik` | `str` | SEC CIK |
|
|
146
|
+
| `name` | `str` | Company name |
|
|
147
|
+
| `ticker` | `str \| None` | Stock ticker |
|
|
148
|
+
| `exchange` | `str \| None` | Exchange |
|
|
149
|
+
| `total_filings` | `int` | Total Form 4 filings |
|
|
150
|
+
| `active_insiders` | `int` | Distinct insiders who have filed |
|
|
151
|
+
| `sic_description` | `str \| None` | SEC SIC industry description |
|
|
152
|
+
| `state_of_incorporation` | `str \| None` | Two-letter state code |
|
|
153
|
+
| `website` | `str \| None` | Company website as filed with SEC |
|
|
154
|
+
|
|
155
|
+
### Pagination
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
# transactions.paginate() — yields one list per page automatically
|
|
159
|
+
all_txns = []
|
|
160
|
+
for batch in client.transactions.paginate(ticker="NVDA", exclude_10b5=True, per_page=500):
|
|
161
|
+
all_txns.extend(batch)
|
|
162
|
+
|
|
163
|
+
# signals.paginate()
|
|
164
|
+
all_signals = []
|
|
165
|
+
for batch in client.signals.paginate(cluster_buy=True, per_page=100):
|
|
166
|
+
all_signals.extend(batch)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Error handling
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from form4api import Form4ApiClient, AuthError, PlanError, RateLimitError, NotFoundError
|
|
173
|
+
|
|
174
|
+
client = Form4ApiClient("YOUR_API_KEY")
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
signals = client.signals.list()
|
|
178
|
+
except PlanError as e:
|
|
179
|
+
print(f"Upgrade required")
|
|
180
|
+
except RateLimitError as e:
|
|
181
|
+
print(f"Retry after {e.retry_after}s")
|
|
182
|
+
except AuthError:
|
|
183
|
+
print("Invalid API key")
|
|
184
|
+
except NotFoundError:
|
|
185
|
+
print("Resource not found")
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
MIT
|
|
@@ -23,6 +23,16 @@ class TransactionsResource:
|
|
|
23
23
|
from_date: str | None = None,
|
|
24
24
|
to_date: str | None = None,
|
|
25
25
|
exclude_10b5: bool | None = None,
|
|
26
|
+
codes: str | None = None,
|
|
27
|
+
exclude_codes: str | None = None,
|
|
28
|
+
category: str | None = None,
|
|
29
|
+
exclude_category: str | None = None,
|
|
30
|
+
exclude_derivative: bool | None = None,
|
|
31
|
+
significant: bool | None = None,
|
|
32
|
+
min_value: float | None = None,
|
|
33
|
+
max_value: float | None = None,
|
|
34
|
+
min_shares: float | None = None,
|
|
35
|
+
max_shares: float | None = None,
|
|
26
36
|
page: int = 1,
|
|
27
37
|
per_page: int = 50,
|
|
28
38
|
) -> list[Transaction]:
|
|
@@ -41,6 +51,26 @@ class TransactionsResource:
|
|
|
41
51
|
params["to"] = to_date
|
|
42
52
|
if exclude_10b5 is not None:
|
|
43
53
|
params["exclude_10b5"] = str(exclude_10b5).lower()
|
|
54
|
+
if codes is not None:
|
|
55
|
+
params["codes"] = codes
|
|
56
|
+
if exclude_codes is not None:
|
|
57
|
+
params["exclude_codes"] = exclude_codes
|
|
58
|
+
if category is not None:
|
|
59
|
+
params["category"] = category
|
|
60
|
+
if exclude_category is not None:
|
|
61
|
+
params["exclude_category"] = exclude_category
|
|
62
|
+
if exclude_derivative is not None:
|
|
63
|
+
params["exclude_derivative"] = str(exclude_derivative).lower()
|
|
64
|
+
if significant is not None:
|
|
65
|
+
params["significant"] = str(significant).lower()
|
|
66
|
+
if min_value is not None:
|
|
67
|
+
params["min_value"] = str(min_value)
|
|
68
|
+
if max_value is not None:
|
|
69
|
+
params["max_value"] = str(max_value)
|
|
70
|
+
if min_shares is not None:
|
|
71
|
+
params["min_shares"] = str(min_shares)
|
|
72
|
+
if max_shares is not None:
|
|
73
|
+
params["max_shares"] = str(max_shares)
|
|
44
74
|
data = self._client._get("/v1/transactions", params)
|
|
45
75
|
return [Transaction(**item) for item in data]
|
|
46
76
|
|
|
@@ -54,6 +84,16 @@ class TransactionsResource:
|
|
|
54
84
|
from_date: str | None = None,
|
|
55
85
|
to_date: str | None = None,
|
|
56
86
|
exclude_10b5: bool | None = None,
|
|
87
|
+
codes: str | None = None,
|
|
88
|
+
exclude_codes: str | None = None,
|
|
89
|
+
category: str | None = None,
|
|
90
|
+
exclude_category: str | None = None,
|
|
91
|
+
exclude_derivative: bool | None = None,
|
|
92
|
+
significant: bool | None = None,
|
|
93
|
+
min_value: float | None = None,
|
|
94
|
+
max_value: float | None = None,
|
|
95
|
+
min_shares: float | None = None,
|
|
96
|
+
max_shares: float | None = None,
|
|
57
97
|
per_page: int = 50,
|
|
58
98
|
) -> Generator[list[Transaction], None, None]:
|
|
59
99
|
page = 1
|
|
@@ -61,7 +101,13 @@ class TransactionsResource:
|
|
|
61
101
|
batch = self.list(
|
|
62
102
|
ticker=ticker, cik=cik, insider_cik=insider_cik,
|
|
63
103
|
code=code, from_date=from_date, to_date=to_date,
|
|
64
|
-
exclude_10b5=exclude_10b5,
|
|
104
|
+
exclude_10b5=exclude_10b5,
|
|
105
|
+
codes=codes, exclude_codes=exclude_codes,
|
|
106
|
+
category=category, exclude_category=exclude_category,
|
|
107
|
+
exclude_derivative=exclude_derivative, significant=significant,
|
|
108
|
+
min_value=min_value, max_value=max_value,
|
|
109
|
+
min_shares=min_shares, max_shares=max_shares,
|
|
110
|
+
page=page, per_page=per_page,
|
|
65
111
|
)
|
|
66
112
|
if not batch:
|
|
67
113
|
break
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: form4api
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Python client for the Form4API — real-time SEC Form 4 insider trading data
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://form4api.com
|
|
7
|
+
Project-URL: Documentation, https://form4api.com/docs
|
|
8
|
+
Project-URL: Repository, https://github.com/theodor90/form4api-py.git
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: httpx>=0.27
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-httpx>=0.30; extra == "dev"
|
|
16
|
+
Requires-Dist: respx>=0.21; extra == "dev"
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# form4api
|
|
20
|
+
|
|
21
|
+
Python client for [Form4API](https://form4api.com) — real-time SEC Form 4 insider trading data.
|
|
22
|
+
|
|
23
|
+
Supports Python 3.11+. Uses `httpx` for both sync and async HTTP.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install form4api
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Sync quickstart
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from form4api import Form4ApiClient
|
|
35
|
+
|
|
36
|
+
client = Form4ApiClient("YOUR_API_KEY")
|
|
37
|
+
|
|
38
|
+
# Recent open-market purchases at Apple (excluding 10b5-1 plan trades)
|
|
39
|
+
txns = client.transactions.list(ticker="AAPL", code="P", exclude_10b5=True, per_page=5)
|
|
40
|
+
for t in txns:
|
|
41
|
+
print(t.insider_name, t.insider_title, t.shares_amount, "@", t.price_per_share)
|
|
42
|
+
print(f" open market: {t.is_open_market}, 10b5 plan: {t.is10b5_plan}, value: ${t.total_value:,.0f}")
|
|
43
|
+
|
|
44
|
+
# Company overview (includes SIC, state, website)
|
|
45
|
+
company = client.companies.get("MSFT")
|
|
46
|
+
print(company.name, company.active_insiders, "active insiders")
|
|
47
|
+
print(company.sic_description, company.state_of_incorporation)
|
|
48
|
+
|
|
49
|
+
# Insider detail
|
|
50
|
+
insider = client.insiders.get("0001234567")
|
|
51
|
+
print(insider.name, insider.officer_title)
|
|
52
|
+
|
|
53
|
+
# Cluster-buy signals (Business plan)
|
|
54
|
+
signals = client.signals.list(cluster_buy=True)
|
|
55
|
+
for sig in signals:
|
|
56
|
+
print(sig.company_name, sig.insider_count, "buyers on", sig.signal_date)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Async quickstart
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import asyncio
|
|
63
|
+
from form4api import AsyncForm4ApiClient
|
|
64
|
+
|
|
65
|
+
async def main():
|
|
66
|
+
async with AsyncForm4ApiClient("YOUR_API_KEY") as client:
|
|
67
|
+
txns = await client.transactions.list(ticker="AAPL", per_page=5)
|
|
68
|
+
for t in txns:
|
|
69
|
+
print(t.insider_name, t.shares_amount, "@", t.price_per_share)
|
|
70
|
+
|
|
71
|
+
asyncio.run(main())
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Resources
|
|
75
|
+
|
|
76
|
+
| Resource | Methods |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `client.transactions` | `.list(**params)`, `.paginate(**params)` |
|
|
79
|
+
| `client.insiders` | `.search(name, **params)`, `.get(cik)`, `.transactions(cik, **params)` |
|
|
80
|
+
| `client.companies` | `.get(ticker)`, `.insiders(ticker)` |
|
|
81
|
+
| `client.signals` | `.list(**params)`, `.paginate(**params)` — Business plan |
|
|
82
|
+
| `client.webhooks` | `.create(url, event_types)`, `.list()`, `.delete(id)`, `.events(**params)` |
|
|
83
|
+
|
|
84
|
+
### Not yet in this SDK
|
|
85
|
+
|
|
86
|
+
The API surface is broader than the typed client. These backend features are **available via the REST API and the `form4api-mcp` server today, but don't have a typed SDK resource yet**:
|
|
87
|
+
|
|
88
|
+
- **Form 144** notice-of-proposed-sale — `GET /v1/form144` *(Business)*
|
|
89
|
+
- **Institutional holdings (13F-HR)** — `GET /v1/holdings`, **managers** — `GET /v1/managers` *(Business)*
|
|
90
|
+
- **Sentiment** (MSPR-style, 10b5-1-clean) — `GET /v1/signals/sentiment/{ticker}` *(Business)*
|
|
91
|
+
- **Insider career summary** — `GET /v1/insiders/{cik}/summary` *(Pro)*
|
|
92
|
+
- **Post-trade returns** (1d/1w/1m/3m/6m) + `min_return_*` screening filters on `/v1/transactions` *(visible free; screening Pro)*
|
|
93
|
+
|
|
94
|
+
Until they land in the SDK, call them directly (`client._get("/v1/holdings", {...})`) or see the [full REST reference](https://form4api.com/docs). For LLM workflows, `form4api-mcp` exposes all of the above as tools.
|
|
95
|
+
|
|
96
|
+
### Transaction filters
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
client.transactions.list(
|
|
100
|
+
ticker="AAPL", # filter by ticker
|
|
101
|
+
cik="0000320193", # or by company CIK
|
|
102
|
+
insider_cik="...", # filter by insider CIK
|
|
103
|
+
code="P", # transaction code: P=purchase, S=sale, A=grant, etc.
|
|
104
|
+
from_date="2026-01-01",
|
|
105
|
+
to_date="2026-12-31",
|
|
106
|
+
exclude_10b5=True, # omit trades filed under a Rule 10b5-1 plan
|
|
107
|
+
per_page=100,
|
|
108
|
+
page=1,
|
|
109
|
+
)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Granular filtering (v0.4.0+)
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
# The "just show me real buys & sells" preset: open-market only,
|
|
116
|
+
# no 10b5-1 plan trades, no derivatives.
|
|
117
|
+
client.transactions.list(ticker="AAPL", significant=True)
|
|
118
|
+
|
|
119
|
+
# Multi-code include / exclude (comma-separated SEC codes)
|
|
120
|
+
client.transactions.list(codes="P,S")
|
|
121
|
+
client.transactions.list(exclude_codes="A,M,F,G")
|
|
122
|
+
|
|
123
|
+
# Whole-category filters: open_market | grants | derivatives | gifts | other
|
|
124
|
+
client.transactions.list(category="open_market")
|
|
125
|
+
client.transactions.list(exclude_category="derivatives")
|
|
126
|
+
client.transactions.list(exclude_derivative=True)
|
|
127
|
+
|
|
128
|
+
# Trade-size screening (Pro plan or higher)
|
|
129
|
+
client.transactions.list(min_value=1_000_000) # USD, shares x price
|
|
130
|
+
client.transactions.list(min_shares=10_000, max_shares=100_000)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Transaction fields
|
|
134
|
+
|
|
135
|
+
| Field | Type | Description |
|
|
136
|
+
|-------|------|-------------|
|
|
137
|
+
| `ticker` | `str` | Stock ticker |
|
|
138
|
+
| `company_name` | `str` | Company name |
|
|
139
|
+
| `insider_name` | `str` | Insider full name |
|
|
140
|
+
| `insider_cik` | `str` | Insider CIK |
|
|
141
|
+
| `insider_title` | `str \| None` | Officer title as reported on the Form 4 |
|
|
142
|
+
| `is_director` | `bool` | Director flag |
|
|
143
|
+
| `is_officer` | `bool` | Officer flag |
|
|
144
|
+
| `is10_pct_owner` | `bool` | 10% owner flag |
|
|
145
|
+
| `accession_number` | `str` | SEC accession number |
|
|
146
|
+
| `security_title` | `str` | Security type |
|
|
147
|
+
| `transaction_code` | `str` | Transaction code |
|
|
148
|
+
| `is_open_market` | `bool` | `True` when code is P or S (not grants/awards) |
|
|
149
|
+
| `is10b5_plan` | `bool` | Filed under a Rule 10b5-1 pre-scheduled trading plan |
|
|
150
|
+
| `shares_amount` | `float` | Shares transacted |
|
|
151
|
+
| `price_per_share` | `float \| None` | Price per share |
|
|
152
|
+
| `total_value` | `float \| None` | `shares_amount × price_per_share` in USD |
|
|
153
|
+
| `shares_owned_after` | `float \| None` | Holdings after transaction |
|
|
154
|
+
| `direct_indirect` | `str \| None` | "D" (direct) or "I" (indirect) |
|
|
155
|
+
| `is_derivative` | `bool` | Derivative security flag |
|
|
156
|
+
| `transaction_date` | `str` | ISO datetime |
|
|
157
|
+
| `period_of_report` | `str` | ISO datetime |
|
|
158
|
+
|
|
159
|
+
### Company fields
|
|
160
|
+
|
|
161
|
+
| Field | Type | Description |
|
|
162
|
+
|-------|------|-------------|
|
|
163
|
+
| `cik` | `str` | SEC CIK |
|
|
164
|
+
| `name` | `str` | Company name |
|
|
165
|
+
| `ticker` | `str \| None` | Stock ticker |
|
|
166
|
+
| `exchange` | `str \| None` | Exchange |
|
|
167
|
+
| `total_filings` | `int` | Total Form 4 filings |
|
|
168
|
+
| `active_insiders` | `int` | Distinct insiders who have filed |
|
|
169
|
+
| `sic_description` | `str \| None` | SEC SIC industry description |
|
|
170
|
+
| `state_of_incorporation` | `str \| None` | Two-letter state code |
|
|
171
|
+
| `website` | `str \| None` | Company website as filed with SEC |
|
|
172
|
+
|
|
173
|
+
### Pagination
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
# transactions.paginate() — yields one list per page automatically
|
|
177
|
+
all_txns = []
|
|
178
|
+
for batch in client.transactions.paginate(ticker="NVDA", exclude_10b5=True, per_page=500):
|
|
179
|
+
all_txns.extend(batch)
|
|
180
|
+
|
|
181
|
+
# signals.paginate()
|
|
182
|
+
all_signals = []
|
|
183
|
+
for batch in client.signals.paginate(cluster_buy=True, per_page=100):
|
|
184
|
+
all_signals.extend(batch)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Error handling
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from form4api import Form4ApiClient, AuthError, PlanError, RateLimitError, NotFoundError
|
|
191
|
+
|
|
192
|
+
client = Form4ApiClient("YOUR_API_KEY")
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
signals = client.signals.list()
|
|
196
|
+
except PlanError as e:
|
|
197
|
+
print(f"Upgrade required")
|
|
198
|
+
except RateLimitError as e:
|
|
199
|
+
print(f"Retry after {e.retry_after}s")
|
|
200
|
+
except AuthError:
|
|
201
|
+
print("Invalid API key")
|
|
202
|
+
except NotFoundError:
|
|
203
|
+
print("Resource not found")
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["setuptools>=68", "wheel"]
|
|
3
|
-
build-backend = "setuptools.build_meta"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "form4api"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "Python client for the Form4API — real-time SEC Form 4 insider trading data"
|
|
9
|
-
requires-python = ">=3.11"
|
|
10
|
-
dependencies = ["httpx>=0.27"]
|
|
11
|
-
license = "MIT"
|
|
12
|
-
readme = "README.md"
|
|
13
|
-
|
|
14
|
-
[project.urls]
|
|
15
|
-
Homepage = "https://form4api.com"
|
|
16
|
-
Documentation = "https://form4api.com/docs"
|
|
17
|
-
Repository = "https://github.com/theodor90/form4api-py.git"
|
|
18
|
-
|
|
19
|
-
[project.optional-dependencies]
|
|
20
|
-
dev = ["pytest>=8", "pytest-httpx>=0.30", "respx>=0.21"]
|
|
21
|
-
|
|
22
|
-
[tool.setuptools.packages.find]
|
|
23
|
-
where = ["."]
|
|
24
|
-
include = ["form4api*"]
|
|
25
|
-
|
|
26
|
-
[tool.pytest.ini_options]
|
|
27
|
-
testpaths = ["tests"]
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "form4api"
|
|
7
|
+
version = "0.4.0"
|
|
8
|
+
description = "Python client for the Form4API — real-time SEC Form 4 insider trading data"
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
dependencies = ["httpx>=0.27"]
|
|
11
|
+
license = "MIT"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
|
|
14
|
+
[project.urls]
|
|
15
|
+
Homepage = "https://form4api.com"
|
|
16
|
+
Documentation = "https://form4api.com/docs"
|
|
17
|
+
Repository = "https://github.com/theodor90/form4api-py.git"
|
|
18
|
+
|
|
19
|
+
[project.optional-dependencies]
|
|
20
|
+
dev = ["pytest>=8", "pytest-httpx>=0.30", "respx>=0.21"]
|
|
21
|
+
|
|
22
|
+
[tool.setuptools.packages.find]
|
|
23
|
+
where = ["."]
|
|
24
|
+
include = ["form4api*"]
|
|
25
|
+
|
|
26
|
+
[tool.pytest.ini_options]
|
|
27
|
+
testpaths = ["tests"]
|
|
@@ -107,6 +107,35 @@ def test_transactions_list_sends_filters(client):
|
|
|
107
107
|
assert qs["per_page"] == "10"
|
|
108
108
|
|
|
109
109
|
|
|
110
|
+
@respx.mock
|
|
111
|
+
def test_transactions_list_sends_granular_filters(client):
|
|
112
|
+
route = respx.get(f"{BASE}/v1/transactions").mock(return_value=httpx.Response(200, json=[]))
|
|
113
|
+
client.transactions.list(
|
|
114
|
+
codes="P,S",
|
|
115
|
+
exclude_codes="A,M",
|
|
116
|
+
category="open_market",
|
|
117
|
+
exclude_category="derivatives",
|
|
118
|
+
exclude_derivative=True,
|
|
119
|
+
significant=True,
|
|
120
|
+
min_value=100000,
|
|
121
|
+
max_value=5000000,
|
|
122
|
+
min_shares=100,
|
|
123
|
+
max_shares=10000,
|
|
124
|
+
)
|
|
125
|
+
assert route.called
|
|
126
|
+
qs = dict(route.calls[0].request.url.params)
|
|
127
|
+
assert qs["codes"] == "P,S"
|
|
128
|
+
assert qs["exclude_codes"] == "A,M"
|
|
129
|
+
assert qs["category"] == "open_market"
|
|
130
|
+
assert qs["exclude_category"] == "derivatives"
|
|
131
|
+
assert qs["exclude_derivative"] == "true"
|
|
132
|
+
assert qs["significant"] == "true"
|
|
133
|
+
assert qs["min_value"] == "100000"
|
|
134
|
+
assert qs["max_value"] == "5000000"
|
|
135
|
+
assert qs["min_shares"] == "100"
|
|
136
|
+
assert qs["max_shares"] == "10000"
|
|
137
|
+
|
|
138
|
+
|
|
110
139
|
@respx.mock
|
|
111
140
|
def test_transactions_paginate_stops_on_short_page(client):
|
|
112
141
|
respx.get(f"{BASE}/v1/transactions").mock(side_effect=[
|
form4api-0.3.1/PKG-INFO
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: form4api
|
|
3
|
-
Version: 0.3.1
|
|
4
|
-
Summary: Python client for the Form4API — real-time SEC Form 4 insider trading data
|
|
5
|
-
License-Expression: MIT
|
|
6
|
-
Project-URL: Homepage, https://form4api.com
|
|
7
|
-
Project-URL: Documentation, https://form4api.com/docs
|
|
8
|
-
Project-URL: Repository, https://github.com/theodor90/form4api-py.git
|
|
9
|
-
Requires-Python: >=3.11
|
|
10
|
-
Description-Content-Type: text/markdown
|
|
11
|
-
License-File: LICENSE
|
|
12
|
-
Requires-Dist: httpx>=0.27
|
|
13
|
-
Provides-Extra: dev
|
|
14
|
-
Requires-Dist: pytest>=8; extra == "dev"
|
|
15
|
-
Requires-Dist: pytest-httpx>=0.30; extra == "dev"
|
|
16
|
-
Requires-Dist: respx>=0.21; extra == "dev"
|
|
17
|
-
Dynamic: license-file
|
|
18
|
-
|
|
19
|
-
# form4api
|
|
20
|
-
|
|
21
|
-
Python client for [Form4API](https://form4api.com) — real-time SEC Form 4 insider trading data.
|
|
22
|
-
|
|
23
|
-
Supports Python 3.11+. Uses `httpx` for both sync and async HTTP.
|
|
24
|
-
|
|
25
|
-
## Installation
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
pip install form4api
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Sync quickstart
|
|
32
|
-
|
|
33
|
-
```python
|
|
34
|
-
from form4api import Form4ApiClient
|
|
35
|
-
|
|
36
|
-
client = Form4ApiClient("YOUR_API_KEY")
|
|
37
|
-
|
|
38
|
-
# Recent open-market purchases at Apple
|
|
39
|
-
txns = client.transactions.list(ticker="AAPL", code="P", per_page=5)
|
|
40
|
-
for t in txns:
|
|
41
|
-
print(t.insider_name, t.shares_amount, "@", t.price_per_share)
|
|
42
|
-
|
|
43
|
-
# Company overview
|
|
44
|
-
company = client.companies.get("MSFT")
|
|
45
|
-
print(company.name, company.active_insiders, "active insiders")
|
|
46
|
-
|
|
47
|
-
# Insider detail
|
|
48
|
-
insider = client.insiders.get("0001234567")
|
|
49
|
-
print(insider.name, insider.officer_title)
|
|
50
|
-
|
|
51
|
-
# Cluster-buy signals (Business plan)
|
|
52
|
-
signals = client.signals.list(ticker="NVDA")
|
|
53
|
-
for sig in signals:
|
|
54
|
-
if sig.is_cluster_buy:
|
|
55
|
-
print(sig.company_name, sig.insider_count, "buyers")
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Async quickstart
|
|
59
|
-
|
|
60
|
-
```python
|
|
61
|
-
import asyncio
|
|
62
|
-
from form4api import AsyncForm4ApiClient
|
|
63
|
-
|
|
64
|
-
async def main():
|
|
65
|
-
async with AsyncForm4ApiClient("YOUR_API_KEY") as client:
|
|
66
|
-
txns = await client.transactions.list(ticker="AAPL", per_page=5)
|
|
67
|
-
for t in txns:
|
|
68
|
-
print(t.insider_name, t.shares_amount, "@", t.price_per_share)
|
|
69
|
-
|
|
70
|
-
asyncio.run(main())
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Resources
|
|
74
|
-
|
|
75
|
-
| Resource | Methods |
|
|
76
|
-
|---|---|
|
|
77
|
-
| `client.transactions` | `.list(**params)`, `.paginate(**params)` |
|
|
78
|
-
| `client.insiders` | `.search(name, **params)`, `.get(cik)`, `.transactions(cik, **params)` |
|
|
79
|
-
| `client.companies` | `.get(ticker)`, `.insiders(ticker)` |
|
|
80
|
-
| `client.signals` | `.list(**params)` — Business plan |
|
|
81
|
-
| `client.webhooks` | `.create(url, event_types)`, `.list()`, `.delete(id)`, `.events(**params)` |
|
|
82
|
-
|
|
83
|
-
## Error handling
|
|
84
|
-
|
|
85
|
-
```python
|
|
86
|
-
from form4api import Form4ApiClient, AuthError, PlanError, RateLimitError, NotFoundError
|
|
87
|
-
|
|
88
|
-
client = Form4ApiClient("YOUR_API_KEY")
|
|
89
|
-
|
|
90
|
-
try:
|
|
91
|
-
signals = client.signals.list()
|
|
92
|
-
except PlanError as e:
|
|
93
|
-
print(f"Upgrade to {e.required_plan}")
|
|
94
|
-
except RateLimitError as e:
|
|
95
|
-
print(f"Retry after {e.retry_after}s")
|
|
96
|
-
except AuthError:
|
|
97
|
-
print("Invalid API key")
|
|
98
|
-
except NotFoundError:
|
|
99
|
-
print("Resource not found")
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## License
|
|
103
|
-
|
|
104
|
-
MIT
|
form4api-0.3.1/README.md
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
# form4api
|
|
2
|
-
|
|
3
|
-
Python client for [Form4API](https://form4api.com) — real-time SEC Form 4 insider trading data.
|
|
4
|
-
|
|
5
|
-
Supports Python 3.11+. Uses `httpx` for both sync and async HTTP.
|
|
6
|
-
|
|
7
|
-
## Installation
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
pip install form4api
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Sync quickstart
|
|
14
|
-
|
|
15
|
-
```python
|
|
16
|
-
from form4api import Form4ApiClient
|
|
17
|
-
|
|
18
|
-
client = Form4ApiClient("YOUR_API_KEY")
|
|
19
|
-
|
|
20
|
-
# Recent open-market purchases at Apple
|
|
21
|
-
txns = client.transactions.list(ticker="AAPL", code="P", per_page=5)
|
|
22
|
-
for t in txns:
|
|
23
|
-
print(t.insider_name, t.shares_amount, "@", t.price_per_share)
|
|
24
|
-
|
|
25
|
-
# Company overview
|
|
26
|
-
company = client.companies.get("MSFT")
|
|
27
|
-
print(company.name, company.active_insiders, "active insiders")
|
|
28
|
-
|
|
29
|
-
# Insider detail
|
|
30
|
-
insider = client.insiders.get("0001234567")
|
|
31
|
-
print(insider.name, insider.officer_title)
|
|
32
|
-
|
|
33
|
-
# Cluster-buy signals (Business plan)
|
|
34
|
-
signals = client.signals.list(ticker="NVDA")
|
|
35
|
-
for sig in signals:
|
|
36
|
-
if sig.is_cluster_buy:
|
|
37
|
-
print(sig.company_name, sig.insider_count, "buyers")
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Async quickstart
|
|
41
|
-
|
|
42
|
-
```python
|
|
43
|
-
import asyncio
|
|
44
|
-
from form4api import AsyncForm4ApiClient
|
|
45
|
-
|
|
46
|
-
async def main():
|
|
47
|
-
async with AsyncForm4ApiClient("YOUR_API_KEY") as client:
|
|
48
|
-
txns = await client.transactions.list(ticker="AAPL", per_page=5)
|
|
49
|
-
for t in txns:
|
|
50
|
-
print(t.insider_name, t.shares_amount, "@", t.price_per_share)
|
|
51
|
-
|
|
52
|
-
asyncio.run(main())
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Resources
|
|
56
|
-
|
|
57
|
-
| Resource | Methods |
|
|
58
|
-
|---|---|
|
|
59
|
-
| `client.transactions` | `.list(**params)`, `.paginate(**params)` |
|
|
60
|
-
| `client.insiders` | `.search(name, **params)`, `.get(cik)`, `.transactions(cik, **params)` |
|
|
61
|
-
| `client.companies` | `.get(ticker)`, `.insiders(ticker)` |
|
|
62
|
-
| `client.signals` | `.list(**params)` — Business plan |
|
|
63
|
-
| `client.webhooks` | `.create(url, event_types)`, `.list()`, `.delete(id)`, `.events(**params)` |
|
|
64
|
-
|
|
65
|
-
## Error handling
|
|
66
|
-
|
|
67
|
-
```python
|
|
68
|
-
from form4api import Form4ApiClient, AuthError, PlanError, RateLimitError, NotFoundError
|
|
69
|
-
|
|
70
|
-
client = Form4ApiClient("YOUR_API_KEY")
|
|
71
|
-
|
|
72
|
-
try:
|
|
73
|
-
signals = client.signals.list()
|
|
74
|
-
except PlanError as e:
|
|
75
|
-
print(f"Upgrade to {e.required_plan}")
|
|
76
|
-
except RateLimitError as e:
|
|
77
|
-
print(f"Retry after {e.retry_after}s")
|
|
78
|
-
except AuthError:
|
|
79
|
-
print("Invalid API key")
|
|
80
|
-
except NotFoundError:
|
|
81
|
-
print("Resource not found")
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
## License
|
|
85
|
-
|
|
86
|
-
MIT
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: form4api
|
|
3
|
-
Version: 0.3.1
|
|
4
|
-
Summary: Python client for the Form4API — real-time SEC Form 4 insider trading data
|
|
5
|
-
License-Expression: MIT
|
|
6
|
-
Project-URL: Homepage, https://form4api.com
|
|
7
|
-
Project-URL: Documentation, https://form4api.com/docs
|
|
8
|
-
Project-URL: Repository, https://github.com/theodor90/form4api-py.git
|
|
9
|
-
Requires-Python: >=3.11
|
|
10
|
-
Description-Content-Type: text/markdown
|
|
11
|
-
License-File: LICENSE
|
|
12
|
-
Requires-Dist: httpx>=0.27
|
|
13
|
-
Provides-Extra: dev
|
|
14
|
-
Requires-Dist: pytest>=8; extra == "dev"
|
|
15
|
-
Requires-Dist: pytest-httpx>=0.30; extra == "dev"
|
|
16
|
-
Requires-Dist: respx>=0.21; extra == "dev"
|
|
17
|
-
Dynamic: license-file
|
|
18
|
-
|
|
19
|
-
# form4api
|
|
20
|
-
|
|
21
|
-
Python client for [Form4API](https://form4api.com) — real-time SEC Form 4 insider trading data.
|
|
22
|
-
|
|
23
|
-
Supports Python 3.11+. Uses `httpx` for both sync and async HTTP.
|
|
24
|
-
|
|
25
|
-
## Installation
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
pip install form4api
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Sync quickstart
|
|
32
|
-
|
|
33
|
-
```python
|
|
34
|
-
from form4api import Form4ApiClient
|
|
35
|
-
|
|
36
|
-
client = Form4ApiClient("YOUR_API_KEY")
|
|
37
|
-
|
|
38
|
-
# Recent open-market purchases at Apple
|
|
39
|
-
txns = client.transactions.list(ticker="AAPL", code="P", per_page=5)
|
|
40
|
-
for t in txns:
|
|
41
|
-
print(t.insider_name, t.shares_amount, "@", t.price_per_share)
|
|
42
|
-
|
|
43
|
-
# Company overview
|
|
44
|
-
company = client.companies.get("MSFT")
|
|
45
|
-
print(company.name, company.active_insiders, "active insiders")
|
|
46
|
-
|
|
47
|
-
# Insider detail
|
|
48
|
-
insider = client.insiders.get("0001234567")
|
|
49
|
-
print(insider.name, insider.officer_title)
|
|
50
|
-
|
|
51
|
-
# Cluster-buy signals (Business plan)
|
|
52
|
-
signals = client.signals.list(ticker="NVDA")
|
|
53
|
-
for sig in signals:
|
|
54
|
-
if sig.is_cluster_buy:
|
|
55
|
-
print(sig.company_name, sig.insider_count, "buyers")
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Async quickstart
|
|
59
|
-
|
|
60
|
-
```python
|
|
61
|
-
import asyncio
|
|
62
|
-
from form4api import AsyncForm4ApiClient
|
|
63
|
-
|
|
64
|
-
async def main():
|
|
65
|
-
async with AsyncForm4ApiClient("YOUR_API_KEY") as client:
|
|
66
|
-
txns = await client.transactions.list(ticker="AAPL", per_page=5)
|
|
67
|
-
for t in txns:
|
|
68
|
-
print(t.insider_name, t.shares_amount, "@", t.price_per_share)
|
|
69
|
-
|
|
70
|
-
asyncio.run(main())
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Resources
|
|
74
|
-
|
|
75
|
-
| Resource | Methods |
|
|
76
|
-
|---|---|
|
|
77
|
-
| `client.transactions` | `.list(**params)`, `.paginate(**params)` |
|
|
78
|
-
| `client.insiders` | `.search(name, **params)`, `.get(cik)`, `.transactions(cik, **params)` |
|
|
79
|
-
| `client.companies` | `.get(ticker)`, `.insiders(ticker)` |
|
|
80
|
-
| `client.signals` | `.list(**params)` — Business plan |
|
|
81
|
-
| `client.webhooks` | `.create(url, event_types)`, `.list()`, `.delete(id)`, `.events(**params)` |
|
|
82
|
-
|
|
83
|
-
## Error handling
|
|
84
|
-
|
|
85
|
-
```python
|
|
86
|
-
from form4api import Form4ApiClient, AuthError, PlanError, RateLimitError, NotFoundError
|
|
87
|
-
|
|
88
|
-
client = Form4ApiClient("YOUR_API_KEY")
|
|
89
|
-
|
|
90
|
-
try:
|
|
91
|
-
signals = client.signals.list()
|
|
92
|
-
except PlanError as e:
|
|
93
|
-
print(f"Upgrade to {e.required_plan}")
|
|
94
|
-
except RateLimitError as e:
|
|
95
|
-
print(f"Retry after {e.retry_after}s")
|
|
96
|
-
except AuthError:
|
|
97
|
-
print("Invalid API key")
|
|
98
|
-
except NotFoundError:
|
|
99
|
-
print("Resource not found")
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## License
|
|
103
|
-
|
|
104
|
-
MIT
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|