freightapis 1.0.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.
- freightapis-1.0.0/LICENSE +21 -0
- freightapis-1.0.0/PKG-INFO +70 -0
- freightapis-1.0.0/README.md +53 -0
- freightapis-1.0.0/freightapis.egg-info/PKG-INFO +70 -0
- freightapis-1.0.0/freightapis.egg-info/SOURCES.txt +9 -0
- freightapis-1.0.0/freightapis.egg-info/dependency_links.txt +1 -0
- freightapis-1.0.0/freightapis.egg-info/requires.txt +3 -0
- freightapis-1.0.0/freightapis.egg-info/top_level.txt +1 -0
- freightapis-1.0.0/freightapis.py +142 -0
- freightapis-1.0.0/pyproject.toml +25 -0
- freightapis-1.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FreightAPIs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: freightapis
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python SDK for FreightAPIs — NMFC freight classification, multi-carrier terminal locations, and global seaport data.
|
|
5
|
+
Author: FreightAPIs
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://freightapis.dev/docs
|
|
8
|
+
Project-URL: Documentation, https://freightapis.dev/docs
|
|
9
|
+
Project-URL: Repository, https://github.com/buildinpublichub/freightapis.dev
|
|
10
|
+
Keywords: freight,nmfc,ltl,shipping,logistics,carrier,terminal,seaport,api
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Provides-Extra: requests
|
|
15
|
+
Requires-Dist: requests>=2; extra == "requests"
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# freightapis (Python)
|
|
19
|
+
|
|
20
|
+
Tiny client for [FreightAPIs](https://freightapis.dev) — NMFC freight classification, multi-carrier terminal locations, and global seaports. Uses `requests` if installed, else the stdlib (zero required dependencies).
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install freightapis
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import os
|
|
30
|
+
from freightapis import FreightAPIs
|
|
31
|
+
|
|
32
|
+
fa = FreightAPIs(os.environ["FREIGHTAPIS_KEY"])
|
|
33
|
+
|
|
34
|
+
# Carrier locations
|
|
35
|
+
ga = fa.carrier_by_state_or_zip("estes", state="GA")
|
|
36
|
+
search = fa.carrier_search("saia", "atlanta")
|
|
37
|
+
cov = fa.carrier_coverage("abf", state="AR")
|
|
38
|
+
batch = fa.carrier_batch("usps", {"states": ["NY", "NJ"]})
|
|
39
|
+
|
|
40
|
+
# Seaports
|
|
41
|
+
cn = fa.ports_by_country("CN")
|
|
42
|
+
sh = fa.port("CNSHA")
|
|
43
|
+
near = fa.ports_nearby(31.2, 121.5, radius=200)
|
|
44
|
+
cosco = fa.ports_by_company("COSCO")
|
|
45
|
+
|
|
46
|
+
# NMFC classification (Pro plan)
|
|
47
|
+
cls = fa.nmfc_search("furniture")
|
|
48
|
+
art = fa.article("63321")
|
|
49
|
+
dens = fa.density({
|
|
50
|
+
"dimensionType": "Inch", "weightType": "Pound", "resultUnitType": "PoundsPerCubicFeet",
|
|
51
|
+
"unitDimensions": [{"HandlingUnitCount": 1, "length": 48, "width": 40, "height": 48, "handlingUnitWeight": 500}],
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
# Any endpoint not covered by a helper:
|
|
55
|
+
days = fa.request("GET", "/api/abf-location/service-days", query={"zip": "72401"})
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Errors
|
|
59
|
+
|
|
60
|
+
Non-2xx responses raise `FreightAPIError` with `.status` and `.body`:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from freightapis import FreightAPIError
|
|
64
|
+
try:
|
|
65
|
+
fa.article("does-not-exist")
|
|
66
|
+
except FreightAPIError as e:
|
|
67
|
+
print(e.status, e.body)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
API reference: https://freightapis.dev/docs · MIT licensed.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# freightapis (Python)
|
|
2
|
+
|
|
3
|
+
Tiny client for [FreightAPIs](https://freightapis.dev) — NMFC freight classification, multi-carrier terminal locations, and global seaports. Uses `requests` if installed, else the stdlib (zero required dependencies).
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install freightapis
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
import os
|
|
13
|
+
from freightapis import FreightAPIs
|
|
14
|
+
|
|
15
|
+
fa = FreightAPIs(os.environ["FREIGHTAPIS_KEY"])
|
|
16
|
+
|
|
17
|
+
# Carrier locations
|
|
18
|
+
ga = fa.carrier_by_state_or_zip("estes", state="GA")
|
|
19
|
+
search = fa.carrier_search("saia", "atlanta")
|
|
20
|
+
cov = fa.carrier_coverage("abf", state="AR")
|
|
21
|
+
batch = fa.carrier_batch("usps", {"states": ["NY", "NJ"]})
|
|
22
|
+
|
|
23
|
+
# Seaports
|
|
24
|
+
cn = fa.ports_by_country("CN")
|
|
25
|
+
sh = fa.port("CNSHA")
|
|
26
|
+
near = fa.ports_nearby(31.2, 121.5, radius=200)
|
|
27
|
+
cosco = fa.ports_by_company("COSCO")
|
|
28
|
+
|
|
29
|
+
# NMFC classification (Pro plan)
|
|
30
|
+
cls = fa.nmfc_search("furniture")
|
|
31
|
+
art = fa.article("63321")
|
|
32
|
+
dens = fa.density({
|
|
33
|
+
"dimensionType": "Inch", "weightType": "Pound", "resultUnitType": "PoundsPerCubicFeet",
|
|
34
|
+
"unitDimensions": [{"HandlingUnitCount": 1, "length": 48, "width": 40, "height": 48, "handlingUnitWeight": 500}],
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
# Any endpoint not covered by a helper:
|
|
38
|
+
days = fa.request("GET", "/api/abf-location/service-days", query={"zip": "72401"})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Errors
|
|
42
|
+
|
|
43
|
+
Non-2xx responses raise `FreightAPIError` with `.status` and `.body`:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from freightapis import FreightAPIError
|
|
47
|
+
try:
|
|
48
|
+
fa.article("does-not-exist")
|
|
49
|
+
except FreightAPIError as e:
|
|
50
|
+
print(e.status, e.body)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
API reference: https://freightapis.dev/docs · MIT licensed.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: freightapis
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python SDK for FreightAPIs — NMFC freight classification, multi-carrier terminal locations, and global seaport data.
|
|
5
|
+
Author: FreightAPIs
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://freightapis.dev/docs
|
|
8
|
+
Project-URL: Documentation, https://freightapis.dev/docs
|
|
9
|
+
Project-URL: Repository, https://github.com/buildinpublichub/freightapis.dev
|
|
10
|
+
Keywords: freight,nmfc,ltl,shipping,logistics,carrier,terminal,seaport,api
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Provides-Extra: requests
|
|
15
|
+
Requires-Dist: requests>=2; extra == "requests"
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# freightapis (Python)
|
|
19
|
+
|
|
20
|
+
Tiny client for [FreightAPIs](https://freightapis.dev) — NMFC freight classification, multi-carrier terminal locations, and global seaports. Uses `requests` if installed, else the stdlib (zero required dependencies).
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install freightapis
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import os
|
|
30
|
+
from freightapis import FreightAPIs
|
|
31
|
+
|
|
32
|
+
fa = FreightAPIs(os.environ["FREIGHTAPIS_KEY"])
|
|
33
|
+
|
|
34
|
+
# Carrier locations
|
|
35
|
+
ga = fa.carrier_by_state_or_zip("estes", state="GA")
|
|
36
|
+
search = fa.carrier_search("saia", "atlanta")
|
|
37
|
+
cov = fa.carrier_coverage("abf", state="AR")
|
|
38
|
+
batch = fa.carrier_batch("usps", {"states": ["NY", "NJ"]})
|
|
39
|
+
|
|
40
|
+
# Seaports
|
|
41
|
+
cn = fa.ports_by_country("CN")
|
|
42
|
+
sh = fa.port("CNSHA")
|
|
43
|
+
near = fa.ports_nearby(31.2, 121.5, radius=200)
|
|
44
|
+
cosco = fa.ports_by_company("COSCO")
|
|
45
|
+
|
|
46
|
+
# NMFC classification (Pro plan)
|
|
47
|
+
cls = fa.nmfc_search("furniture")
|
|
48
|
+
art = fa.article("63321")
|
|
49
|
+
dens = fa.density({
|
|
50
|
+
"dimensionType": "Inch", "weightType": "Pound", "resultUnitType": "PoundsPerCubicFeet",
|
|
51
|
+
"unitDimensions": [{"HandlingUnitCount": 1, "length": 48, "width": 40, "height": 48, "handlingUnitWeight": 500}],
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
# Any endpoint not covered by a helper:
|
|
55
|
+
days = fa.request("GET", "/api/abf-location/service-days", query={"zip": "72401"})
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Errors
|
|
59
|
+
|
|
60
|
+
Non-2xx responses raise `FreightAPIError` with `.status` and `.body`:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from freightapis import FreightAPIError
|
|
64
|
+
try:
|
|
65
|
+
fa.article("does-not-exist")
|
|
66
|
+
except FreightAPIError as e:
|
|
67
|
+
print(e.status, e.body)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
API reference: https://freightapis.dev/docs · MIT licensed.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
freightapis
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FreightAPIs Python SDK — a tiny dependency-light client for the FreightAPIs
|
|
3
|
+
REST API (https://freightapis.dev).
|
|
4
|
+
|
|
5
|
+
Uses `requests` if available, else falls back to the stdlib `urllib`, so it
|
|
6
|
+
works with zero third-party dependencies.
|
|
7
|
+
|
|
8
|
+
Get an API key at https://freightapis.dev/account. Full reference:
|
|
9
|
+
https://freightapis.dev/docs · OpenAPI: https://freightapis.dev/openapi.yaml
|
|
10
|
+
|
|
11
|
+
MIT licensed.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json as _json
|
|
16
|
+
from typing import Any, Optional, Mapping
|
|
17
|
+
from urllib.parse import urlencode
|
|
18
|
+
|
|
19
|
+
DEFAULT_BASE_URL = "https://freightapis.dev"
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
import requests as _requests # type: ignore
|
|
23
|
+
except ImportError: # pragma: no cover
|
|
24
|
+
_requests = None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class FreightAPIError(Exception):
|
|
28
|
+
def __init__(self, message: str, status: int, body: Any = None):
|
|
29
|
+
super().__init__(message)
|
|
30
|
+
self.status = status
|
|
31
|
+
self.body = body
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class FreightAPIs:
|
|
35
|
+
"""Client for the FreightAPIs REST API."""
|
|
36
|
+
|
|
37
|
+
def __init__(self, api_key: str, base_url: str = DEFAULT_BASE_URL):
|
|
38
|
+
if not api_key:
|
|
39
|
+
raise ValueError("FreightAPIs: an API key is required")
|
|
40
|
+
self.api_key = api_key
|
|
41
|
+
self.base_url = base_url.rstrip("/")
|
|
42
|
+
|
|
43
|
+
# ── transport ────────────────────────────────────────────────────────
|
|
44
|
+
def _request(self, method: str, path: str, query: Optional[Mapping[str, Any]] = None,
|
|
45
|
+
body: Any = None) -> Any:
|
|
46
|
+
url = self.base_url + path
|
|
47
|
+
if query:
|
|
48
|
+
clean = {k: v for k, v in query.items() if v is not None}
|
|
49
|
+
if clean:
|
|
50
|
+
url += "?" + urlencode(clean)
|
|
51
|
+
headers = {"X-API-Key": self.api_key, "Accept": "application/json"}
|
|
52
|
+
payload = None
|
|
53
|
+
if body is not None:
|
|
54
|
+
headers["Content-Type"] = "application/json"
|
|
55
|
+
payload = _json.dumps(body)
|
|
56
|
+
|
|
57
|
+
if _requests is not None:
|
|
58
|
+
resp = _requests.request(method, url, headers=headers, data=payload)
|
|
59
|
+
status, text = resp.status_code, resp.text
|
|
60
|
+
else:
|
|
61
|
+
import urllib.request
|
|
62
|
+
import urllib.error
|
|
63
|
+
req = urllib.request.Request(
|
|
64
|
+
url, data=payload.encode() if payload else None,
|
|
65
|
+
headers=headers, method=method)
|
|
66
|
+
try:
|
|
67
|
+
with urllib.request.urlopen(req) as r:
|
|
68
|
+
status, text = r.status, r.read().decode()
|
|
69
|
+
except urllib.error.HTTPError as e:
|
|
70
|
+
status, text = e.code, e.read().decode()
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
data = _json.loads(text) if text else None
|
|
74
|
+
except ValueError:
|
|
75
|
+
data = text
|
|
76
|
+
if not (200 <= status < 300):
|
|
77
|
+
msg = (data.get("error") if isinstance(data, dict) else None) or f"HTTP {status}"
|
|
78
|
+
raise FreightAPIError(msg, status, data)
|
|
79
|
+
return data
|
|
80
|
+
|
|
81
|
+
# ── carrier locations ────────────────────────────────────────────────
|
|
82
|
+
def carrier_by_state_or_zip(self, carrier: str, *, state: Optional[str] = None,
|
|
83
|
+
zip: Optional[str] = None, country: Optional[str] = None) -> Any:
|
|
84
|
+
return self._request("GET", f"/api/{carrier}-location",
|
|
85
|
+
{"state": state, "zip": zip, "country": country})
|
|
86
|
+
|
|
87
|
+
def carrier_search(self, carrier: str, query: str, page: int = 1) -> Any:
|
|
88
|
+
return self._request("GET", f"/api/{carrier}-location/search", {"q": query, "page": page})
|
|
89
|
+
|
|
90
|
+
def carrier_coverage(self, carrier: str, *, state: Optional[str] = None) -> Any:
|
|
91
|
+
return self._request("GET", f"/api/{carrier}-location/coverage", {"state": state})
|
|
92
|
+
|
|
93
|
+
def carrier_batch(self, carrier: str, body: Mapping[str, Any]) -> Any:
|
|
94
|
+
return self._request("POST", f"/api/{carrier}-location/batch", body=body)
|
|
95
|
+
|
|
96
|
+
# ── seaports ──────────────────────────────────────────────────────────
|
|
97
|
+
def ports_by_country(self, country: str, page: int = 1) -> Any:
|
|
98
|
+
return self._request("GET", "/api/ports", {"country": country, "page": page})
|
|
99
|
+
|
|
100
|
+
def ports_search(self, query: str, page: int = 1) -> Any:
|
|
101
|
+
return self._request("GET", "/api/ports/search", {"q": query, "page": page})
|
|
102
|
+
|
|
103
|
+
def ports_by_company(self, company: str, page: int = 1) -> Any:
|
|
104
|
+
return self._request("GET", "/api/ports/by-company", {"company": company, "page": page})
|
|
105
|
+
|
|
106
|
+
def ports_nearby(self, lat: float, lng: float, *, radius: Optional[float] = None,
|
|
107
|
+
page: Optional[int] = None) -> Any:
|
|
108
|
+
return self._request("GET", "/api/ports/nearby",
|
|
109
|
+
{"lat": lat, "lng": lng, "radius": radius, "page": page})
|
|
110
|
+
|
|
111
|
+
def port(self, unlocode: str) -> Any:
|
|
112
|
+
return self._request("GET", f"/api/ports/{unlocode}")
|
|
113
|
+
|
|
114
|
+
def ports_batch(self, unlocodes: list[str]) -> Any:
|
|
115
|
+
return self._request("POST", "/api/ports/batch", body={"unlocodes": unlocodes})
|
|
116
|
+
|
|
117
|
+
# ── NMFC classification (Pro plan) ────────────────────────────────────
|
|
118
|
+
def nmfc_search(self, query: str, page: int = 1) -> Any:
|
|
119
|
+
return self._request("POST", "/api/search", body={"q": query, "page": page})
|
|
120
|
+
|
|
121
|
+
def article(self, num: Any) -> Any:
|
|
122
|
+
return self._request("GET", f"/api/article/{num}")
|
|
123
|
+
|
|
124
|
+
def rule(self, num: Any) -> Any:
|
|
125
|
+
return self._request("GET", f"/api/rule/{num}")
|
|
126
|
+
|
|
127
|
+
def package(self, num: Any) -> Any:
|
|
128
|
+
return self._request("GET", f"/api/package/{num}")
|
|
129
|
+
|
|
130
|
+
def history(self, num: Any) -> Any:
|
|
131
|
+
return self._request("GET", f"/api/history/{num}")
|
|
132
|
+
|
|
133
|
+
def density(self, body: Any) -> Any:
|
|
134
|
+
return self._request("POST", "/api/density", body=body)
|
|
135
|
+
|
|
136
|
+
def item680(self, type: str, body: Any) -> Any:
|
|
137
|
+
return self._request("POST", f"/api/item680/{type}", body=body)
|
|
138
|
+
|
|
139
|
+
# ── escape hatch ──────────────────────────────────────────────────────
|
|
140
|
+
def request(self, method: str, path: str, *, query: Optional[Mapping[str, Any]] = None,
|
|
141
|
+
body: Any = None) -> Any:
|
|
142
|
+
return self._request(method, path, query, body)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "freightapis"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Python SDK for FreightAPIs — NMFC freight classification, multi-carrier terminal locations, and global seaport data."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.8"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
keywords = ["freight", "nmfc", "ltl", "shipping", "logistics", "carrier", "terminal", "seaport", "api"]
|
|
9
|
+
authors = [{ name = "FreightAPIs" }]
|
|
10
|
+
dependencies = []
|
|
11
|
+
|
|
12
|
+
[project.optional-dependencies]
|
|
13
|
+
requests = ["requests>=2"]
|
|
14
|
+
|
|
15
|
+
[project.urls]
|
|
16
|
+
Homepage = "https://freightapis.dev/docs"
|
|
17
|
+
Documentation = "https://freightapis.dev/docs"
|
|
18
|
+
Repository = "https://github.com/buildinpublichub/freightapis.dev"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["setuptools>=61"]
|
|
22
|
+
build-backend = "setuptools.build_meta"
|
|
23
|
+
|
|
24
|
+
[tool.setuptools]
|
|
25
|
+
py-modules = ["freightapis"]
|