reelspan 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.
reelspan-1.0.0/LICENSE ADDED
@@ -0,0 +1,16 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anjum Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8
+ persons to whom the Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11
+ Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,167 @@
1
+ Metadata-Version: 2.4
2
+ Name: reelspan
3
+ Version: 1.0.0
4
+ Summary: Official Python client for the Reelspan API: publish videos, photos and text to TikTok, YouTube, Instagram, Facebook, X, Threads, LinkedIn, Pinterest, Bluesky, Reddit and more from one call.
5
+ Author-email: Anjum Labs <support@reelspan.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://reelspan.com
8
+ Project-URL: Source, https://github.com/Reelspan/reelspan-python
9
+ Project-URL: Issues, https://github.com/Reelspan/reelspan-python/issues
10
+ Project-URL: Documentation, https://reelspan.com/docs/sdks
11
+ Project-URL: Changelog, https://reelspan.com/docs/sdks#changelog
12
+ Keywords: reelspan,social media,social media api,publishing,scheduling,tiktok,youtube,instagram,linkedin,bluesky,reddit,upload-post
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Topic :: Internet :: WWW/HTTP
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Dynamic: license-file
25
+
26
+ # reelspan
27
+
28
+ The official Python client for the [Reelspan API](https://reelspan.com/docs). Publish one video, photo set or text
29
+ post to TikTok, YouTube, Instagram, Facebook, X, Threads, LinkedIn, Pinterest, Bluesky, Reddit, Google Business,
30
+ Telegram, Discord, Slack, Mastodon, Nostr, Lemmy, Dev.to, Hashnode, WordPress, Whop and Listmonk, schedule it, and read
31
+ how it did.
32
+
33
+ - Standard library only. Python 3.10+, fully typed.
34
+ - Retries network errors, 429s and 5xx answers with backoff, honouring `Retry-After`.
35
+ - Every post carries an `Idempotency-Key`, so a retried post is never published twice.
36
+ - Large files go straight to storage in parts, read from disk piece by piece, with progress and per-part retries.
37
+
38
+ ```bash
39
+ pip install reelspan
40
+ ```
41
+
42
+ ## Quick start
43
+
44
+ ```python
45
+ from reelspan import Reelspan
46
+
47
+ reelspan = Reelspan() # reads REELSPAN_API_KEY
48
+
49
+ post = reelspan.posts.video(
50
+ user="my-brand", # a profile's username
51
+ platforms=["tiktok", "youtube", "instagram"],
52
+ video="launch.mp4", # a path, (name, bytes), or a public https URL
53
+ title="Launch day",
54
+ )
55
+ for platform, result in post["results"].items():
56
+ print(platform, result.get("url") if result["success"] else result.get("error"))
57
+ ```
58
+
59
+ ## Posting
60
+
61
+ ```python
62
+ # photos or a carousel, in order, with alt text
63
+ reelspan.posts.photos("my-brand", ["instagram", "pinterest"], ["front.jpg", "back.jpg"], "New colours", alt_texts=["Front", "Back"], extra={"pinterest_board_id": "123"})
64
+
65
+ # text: long posts become threads on X and Bluesky
66
+ reelspan.posts.text("my-brand", ["x", "bluesky", "threads", "linkedin"], "We shipped it.")
67
+
68
+ # a PDF to LinkedIn as a swipeable document
69
+ reelspan.posts.document("my-brand", "q3-report.pdf", "Q3 report")
70
+
71
+ # schedule, queue or repeat
72
+ reelspan.posts.video("my-brand", ["tiktok"], "clip.mp4", scheduled_date="2026-12-01T09:00:00Z")
73
+ reelspan.posts.video("my-brand", ["tiktok"], "clip.mp4", add_to_queue=True)
74
+ reelspan.posts.text("my-brand", ["x"], "Weekly tip", scheduled_date=when, repeat_every="week", repeat_times=6)
75
+
76
+ # network settings go in extra
77
+ reelspan.posts.video("my-brand", ["reddit", "tiktok"], "clip.mp4", "Clip", extra={"reddit_subreddit": "videos", "privacy_level": "PUBLIC_TO_EVERYONE"})
78
+ ```
79
+
80
+ Pass `run_async=True` to get a `request_id` at once, then:
81
+
82
+ ```python
83
+ request_id = reelspan.posts.video("my-brand", ["youtube"], "long.mp4", run_async=True)["request_id"]
84
+ done = reelspan.posts.wait(request_id) # polls until every network has finished
85
+ reelspan.posts.retry(request_id) # sends the failed networks again
86
+ ```
87
+
88
+ ## Large files
89
+
90
+ Files from 90 MB go up in parts on their own. To upload once and reuse:
91
+
92
+ ```python
93
+ media = reelspan.media.upload("film.mp4", kind="video", on_progress=lambda sent, total: print(f"{sent * 100 // total}%"))
94
+ reelspan.posts.video("my-brand", ["youtube"], video_media_id=media["media_id"], title="The film")
95
+ ```
96
+
97
+ ## Profiles and accounts
98
+
99
+ ```python
100
+ reelspan.profiles.create("client-a", "Client A")
101
+
102
+ # networks that sign in with OAuth: send your customer to a hosted, branded page
103
+ link = reelspan.connect.link("client-a", logo_image="https://…", redirect_url="https://you.com/done")["access_url"]
104
+
105
+ # networks that use credentials: connect them straight from your code
106
+ reelspan.accounts.connect("client-a", "discord", webhook_url="https://discord.com/api/webhooks/…")
107
+ reelspan.accounts.connect("client-a", "mastodon", instance_url="mastodon.social", access_token="…")
108
+
109
+ reelspan.accounts.disconnect("client-a", "discord")
110
+ ```
111
+
112
+ ## History, analytics and scheduling
113
+
114
+ ```python
115
+ for row in reelspan.posts.all(platform="tiktok", status="failed"):
116
+ print(row)
117
+ reelspan.analytics.account("my-brand")
118
+ reelspan.analytics.best_times("my-brand", timezone="Asia/Karachi")
119
+ reelspan.scheduled.list(profile_username="my-brand")
120
+ reelspan.scheduled.cancel(job_id, with_later_repeats=True)
121
+ ```
122
+
123
+ Also: `queue`, `library`, `approvals`, `ai` (captions from a video or your text), `pinterest.create_board`, `webhooks`
124
+ (account and per-profile), and `request(method, path)` for anything else.
125
+
126
+ ## Webhooks
127
+
128
+ ```python
129
+ from reelspan import verify_webhook
130
+
131
+ @app.post("/reelspan")
132
+ def hook(request):
133
+ ok = verify_webhook(request.body, request.headers.get("X-Reelspan-Signature"), request.headers.get("X-Reelspan-Timestamp"), SECRET)
134
+ if not ok:
135
+ return Response(status=401)
136
+ ...
137
+ ```
138
+
139
+ ## Errors
140
+
141
+ ```python
142
+ from reelspan import ReelspanError, ReelspanConnectionError
143
+
144
+ try:
145
+ reelspan.posts.text("my-brand", ["x"], "Hi")
146
+ except ReelspanError as e:
147
+ print(e.status, e.code, e, e.retry_after)
148
+ except ReelspanConnectionError as e:
149
+ print("no answer:", e)
150
+ ```
151
+
152
+ ## Options
153
+
154
+ ```python
155
+ Reelspan(
156
+ api_key="rs_live_…", # or REELSPAN_API_KEY
157
+ base_url="https://api.reelspan.com",
158
+ timeout=120, # seconds per attempt
159
+ max_retries=2,
160
+ headers={"X-My-App": "scheduler"},
161
+ )
162
+ ```
163
+
164
+ Use a test key (`rs_test_…`) to try everything without publishing. `reelspan.rate_limit` holds the limit the API
165
+ reported last. Full reference: [reelspan.com/docs](https://reelspan.com/docs).
166
+
167
+ MIT licensed.
@@ -0,0 +1,142 @@
1
+ # reelspan
2
+
3
+ The official Python client for the [Reelspan API](https://reelspan.com/docs). Publish one video, photo set or text
4
+ post to TikTok, YouTube, Instagram, Facebook, X, Threads, LinkedIn, Pinterest, Bluesky, Reddit, Google Business,
5
+ Telegram, Discord, Slack, Mastodon, Nostr, Lemmy, Dev.to, Hashnode, WordPress, Whop and Listmonk, schedule it, and read
6
+ how it did.
7
+
8
+ - Standard library only. Python 3.10+, fully typed.
9
+ - Retries network errors, 429s and 5xx answers with backoff, honouring `Retry-After`.
10
+ - Every post carries an `Idempotency-Key`, so a retried post is never published twice.
11
+ - Large files go straight to storage in parts, read from disk piece by piece, with progress and per-part retries.
12
+
13
+ ```bash
14
+ pip install reelspan
15
+ ```
16
+
17
+ ## Quick start
18
+
19
+ ```python
20
+ from reelspan import Reelspan
21
+
22
+ reelspan = Reelspan() # reads REELSPAN_API_KEY
23
+
24
+ post = reelspan.posts.video(
25
+ user="my-brand", # a profile's username
26
+ platforms=["tiktok", "youtube", "instagram"],
27
+ video="launch.mp4", # a path, (name, bytes), or a public https URL
28
+ title="Launch day",
29
+ )
30
+ for platform, result in post["results"].items():
31
+ print(platform, result.get("url") if result["success"] else result.get("error"))
32
+ ```
33
+
34
+ ## Posting
35
+
36
+ ```python
37
+ # photos or a carousel, in order, with alt text
38
+ reelspan.posts.photos("my-brand", ["instagram", "pinterest"], ["front.jpg", "back.jpg"], "New colours", alt_texts=["Front", "Back"], extra={"pinterest_board_id": "123"})
39
+
40
+ # text: long posts become threads on X and Bluesky
41
+ reelspan.posts.text("my-brand", ["x", "bluesky", "threads", "linkedin"], "We shipped it.")
42
+
43
+ # a PDF to LinkedIn as a swipeable document
44
+ reelspan.posts.document("my-brand", "q3-report.pdf", "Q3 report")
45
+
46
+ # schedule, queue or repeat
47
+ reelspan.posts.video("my-brand", ["tiktok"], "clip.mp4", scheduled_date="2026-12-01T09:00:00Z")
48
+ reelspan.posts.video("my-brand", ["tiktok"], "clip.mp4", add_to_queue=True)
49
+ reelspan.posts.text("my-brand", ["x"], "Weekly tip", scheduled_date=when, repeat_every="week", repeat_times=6)
50
+
51
+ # network settings go in extra
52
+ reelspan.posts.video("my-brand", ["reddit", "tiktok"], "clip.mp4", "Clip", extra={"reddit_subreddit": "videos", "privacy_level": "PUBLIC_TO_EVERYONE"})
53
+ ```
54
+
55
+ Pass `run_async=True` to get a `request_id` at once, then:
56
+
57
+ ```python
58
+ request_id = reelspan.posts.video("my-brand", ["youtube"], "long.mp4", run_async=True)["request_id"]
59
+ done = reelspan.posts.wait(request_id) # polls until every network has finished
60
+ reelspan.posts.retry(request_id) # sends the failed networks again
61
+ ```
62
+
63
+ ## Large files
64
+
65
+ Files from 90 MB go up in parts on their own. To upload once and reuse:
66
+
67
+ ```python
68
+ media = reelspan.media.upload("film.mp4", kind="video", on_progress=lambda sent, total: print(f"{sent * 100 // total}%"))
69
+ reelspan.posts.video("my-brand", ["youtube"], video_media_id=media["media_id"], title="The film")
70
+ ```
71
+
72
+ ## Profiles and accounts
73
+
74
+ ```python
75
+ reelspan.profiles.create("client-a", "Client A")
76
+
77
+ # networks that sign in with OAuth: send your customer to a hosted, branded page
78
+ link = reelspan.connect.link("client-a", logo_image="https://…", redirect_url="https://you.com/done")["access_url"]
79
+
80
+ # networks that use credentials: connect them straight from your code
81
+ reelspan.accounts.connect("client-a", "discord", webhook_url="https://discord.com/api/webhooks/…")
82
+ reelspan.accounts.connect("client-a", "mastodon", instance_url="mastodon.social", access_token="…")
83
+
84
+ reelspan.accounts.disconnect("client-a", "discord")
85
+ ```
86
+
87
+ ## History, analytics and scheduling
88
+
89
+ ```python
90
+ for row in reelspan.posts.all(platform="tiktok", status="failed"):
91
+ print(row)
92
+ reelspan.analytics.account("my-brand")
93
+ reelspan.analytics.best_times("my-brand", timezone="Asia/Karachi")
94
+ reelspan.scheduled.list(profile_username="my-brand")
95
+ reelspan.scheduled.cancel(job_id, with_later_repeats=True)
96
+ ```
97
+
98
+ Also: `queue`, `library`, `approvals`, `ai` (captions from a video or your text), `pinterest.create_board`, `webhooks`
99
+ (account and per-profile), and `request(method, path)` for anything else.
100
+
101
+ ## Webhooks
102
+
103
+ ```python
104
+ from reelspan import verify_webhook
105
+
106
+ @app.post("/reelspan")
107
+ def hook(request):
108
+ ok = verify_webhook(request.body, request.headers.get("X-Reelspan-Signature"), request.headers.get("X-Reelspan-Timestamp"), SECRET)
109
+ if not ok:
110
+ return Response(status=401)
111
+ ...
112
+ ```
113
+
114
+ ## Errors
115
+
116
+ ```python
117
+ from reelspan import ReelspanError, ReelspanConnectionError
118
+
119
+ try:
120
+ reelspan.posts.text("my-brand", ["x"], "Hi")
121
+ except ReelspanError as e:
122
+ print(e.status, e.code, e, e.retry_after)
123
+ except ReelspanConnectionError as e:
124
+ print("no answer:", e)
125
+ ```
126
+
127
+ ## Options
128
+
129
+ ```python
130
+ Reelspan(
131
+ api_key="rs_live_…", # or REELSPAN_API_KEY
132
+ base_url="https://api.reelspan.com",
133
+ timeout=120, # seconds per attempt
134
+ max_retries=2,
135
+ headers={"X-My-App": "scheduler"},
136
+ )
137
+ ```
138
+
139
+ Use a test key (`rs_test_…`) to try everything without publishing. `reelspan.rate_limit` holds the limit the API
140
+ reported last. Full reference: [reelspan.com/docs](https://reelspan.com/docs).
141
+
142
+ MIT licensed.
@@ -0,0 +1,41 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "reelspan"
7
+ dynamic = ["version"]
8
+ description = "Official Python client for the Reelspan API: publish videos, photos and text to TikTok, YouTube, Instagram, Facebook, X, Threads, LinkedIn, Pinterest, Bluesky, Reddit and more from one call."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Anjum Labs", email = "support@reelspan.com" }]
14
+ keywords = ["reelspan", "social media", "social media api", "publishing", "scheduling", "tiktok", "youtube", "instagram", "linkedin", "bluesky", "reddit", "upload-post"]
15
+ classifiers = [
16
+ "Development Status :: 5 - Production/Stable",
17
+ "Intended Audience :: Developers",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3 :: Only",
21
+ "Topic :: Internet :: WWW/HTTP",
22
+ "Topic :: Software Development :: Libraries :: Python Modules",
23
+ "Typing :: Typed",
24
+ ]
25
+ dependencies = []
26
+
27
+ [project.urls]
28
+ Homepage = "https://reelspan.com"
29
+ Source = "https://github.com/Reelspan/reelspan-python"
30
+ Issues = "https://github.com/Reelspan/reelspan-python/issues"
31
+ Documentation = "https://reelspan.com/docs/sdks"
32
+ Changelog = "https://reelspan.com/docs/sdks#changelog"
33
+
34
+ [tool.setuptools]
35
+ packages = ["reelspan"]
36
+
37
+ [tool.setuptools.dynamic]
38
+ version = { attr = "reelspan._version.__version__" }
39
+
40
+ [tool.setuptools.package-data]
41
+ reelspan = ["py.typed"]
@@ -0,0 +1,10 @@
1
+ """The official Python client for the Reelspan API (https://api.reelspan.com/v1).
2
+
3
+ Publish one video, photo set or text post to TikTok, YouTube, Instagram, Facebook, X, Threads, LinkedIn, Pinterest,
4
+ Bluesky, Reddit and more, schedule it, and read how it did. Standard library only.
5
+ """
6
+
7
+ from ._version import __version__
8
+ from .client import DIRECT_UPLOAD_BYTES, RateLimit, Reelspan, ReelspanConnectionError, ReelspanError, verify_webhook
9
+
10
+ __all__ = ["Reelspan", "ReelspanError", "ReelspanConnectionError", "RateLimit", "verify_webhook", "DIRECT_UPLOAD_BYTES", "__version__"]
@@ -0,0 +1 @@
1
+ __version__ = "1.0.0"