upload-post 2.8.0__tar.gz → 2.9.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.
- {upload_post-2.8.0 → upload_post-2.9.0}/PKG-INFO +80 -5
- {upload_post-2.8.0 → upload_post-2.9.0}/README.md +79 -4
- {upload_post-2.8.0 → upload_post-2.9.0}/setup.py +1 -1
- {upload_post-2.8.0 → upload_post-2.9.0}/upload_post/__init__.py +1 -1
- {upload_post-2.8.0 → upload_post-2.9.0}/upload_post/api_client.py +169 -23
- {upload_post-2.8.0 → upload_post-2.9.0}/upload_post.egg-info/PKG-INFO +80 -5
- {upload_post-2.8.0 → upload_post-2.9.0}/setup.cfg +0 -0
- {upload_post-2.8.0 → upload_post-2.9.0}/upload_post/cli.py +0 -0
- {upload_post-2.8.0 → upload_post-2.9.0}/upload_post.egg-info/SOURCES.txt +0 -0
- {upload_post-2.8.0 → upload_post-2.9.0}/upload_post.egg-info/dependency_links.txt +0 -0
- {upload_post-2.8.0 → upload_post-2.9.0}/upload_post.egg-info/requires.txt +0 -0
- {upload_post-2.8.0 → upload_post-2.9.0}/upload_post.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: upload-post
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.0
|
|
4
4
|
Summary: Cross-platform social media upload for TikTok, Instagram, YouTube, LinkedIn, Facebook, Pinterest, Threads, Reddit, Bluesky, Discord, Telegram, and X (Twitter)
|
|
5
5
|
Home-page: https://www.upload-post.com/
|
|
6
6
|
Author: Upload-Post
|
|
@@ -211,9 +211,16 @@ jwt = client.generate_jwt(
|
|
|
211
211
|
redirect_url="https://yourapp.com/callback",
|
|
212
212
|
platforms=["tiktok", "instagram"],
|
|
213
213
|
# Optional: force the connection page language for this profile.
|
|
214
|
-
# Supported: "en", "es", "de", "fr", "pt". When omitted, the page
|
|
214
|
+
# Supported: "en", "es", "de", "fr", "pt", "pl", "tr". When omitted, the page
|
|
215
215
|
# auto-detects the visitor's browser language and falls back to English.
|
|
216
216
|
language="es",
|
|
217
|
+
# Optional: override individual connection-page strings. Flat dict of i18n
|
|
218
|
+
# dot-path keys to strings. Max 100 entries, keys ^[a-zA-Z0-9_.]+$, values
|
|
219
|
+
# up to 300 chars. Echoed back in the "profile" object of validate_jwt.
|
|
220
|
+
ui_labels={
|
|
221
|
+
"connect.title": "Link your accounts",
|
|
222
|
+
"connect.subtitle": "Publish everywhere from one place",
|
|
223
|
+
},
|
|
217
224
|
)
|
|
218
225
|
```
|
|
219
226
|
|
|
@@ -225,6 +232,35 @@ analytics = client.get_analytics(
|
|
|
225
232
|
platforms=["instagram", "tiktok"],
|
|
226
233
|
)
|
|
227
234
|
print(analytics)
|
|
235
|
+
|
|
236
|
+
# Instagram returns two audience breakdowns with the same shape
|
|
237
|
+
# ("age", "gender", "country", "city"):
|
|
238
|
+
print(analytics["analytics"]["instagram"]["follower_demographics"])
|
|
239
|
+
print(analytics["analytics"]["instagram"]["engaged_audience_demographics"])
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Cached Post Analytics
|
|
243
|
+
|
|
244
|
+
Replays per-post metrics already fetched, instead of calling the platforms again. Only contains posts previously fetched through a live per-post endpoint; there is no background refresh, so captured_at is the last time that post was read live. Not subject to the live
|
|
245
|
+
calls, so it is not subject to the live post-analytics rate limit
|
|
246
|
+
(100 requests / 5 minutes). Use it to page through a profile's post history.
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
cursor = None
|
|
250
|
+
while True:
|
|
251
|
+
page = client.get_cached_post_analytics(
|
|
252
|
+
"my-profile",
|
|
253
|
+
platform="youtube", # optional: instagram, tiktok, youtube, facebook, linkedin, threads, pinterest, reddit
|
|
254
|
+
limit=50, # default 50, max 200
|
|
255
|
+
since="2026-06-01", # defaults to 30 days ago
|
|
256
|
+
until="2026-07-01", # defaults to today
|
|
257
|
+
cursor=cursor,
|
|
258
|
+
)
|
|
259
|
+
for post in page["posts"]:
|
|
260
|
+
print(post["platform"], post["post_id"], post["metrics"])
|
|
261
|
+
cursor = page["next_cursor"]
|
|
262
|
+
if not cursor:
|
|
263
|
+
break
|
|
228
264
|
```
|
|
229
265
|
|
|
230
266
|
### Get Media
|
|
@@ -244,6 +280,24 @@ media = client.get_media("linkedin", "my-profile", page_urn="me")
|
|
|
244
280
|
media = client.get_media("linkedin", "my-profile", page_urn="12345")
|
|
245
281
|
```
|
|
246
282
|
|
|
283
|
+
The response carries a `pagination` object — `{"limit": ..., "next_cursor": ...,
|
|
284
|
+
"has_more": ...}`, with `next_cursor` `None` and `has_more` `False` on the last
|
|
285
|
+
page:
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
cursor = None
|
|
289
|
+
while True:
|
|
290
|
+
page = client.get_media("instagram", "my-profile", limit=50, cursor=cursor)
|
|
291
|
+
print(len(page["media"]))
|
|
292
|
+
cursor = page["pagination"]["next_cursor"]
|
|
293
|
+
if not cursor:
|
|
294
|
+
break
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
`limit` defaults to 25 and is clamped to 1-100, with per-platform caps of 20 for
|
|
298
|
+
TikTok and 50 for YouTube. **LinkedIn, Discord and Telegram do not support
|
|
299
|
+
cursors** — they accept `limit` only, and passing a `cursor` returns HTTP 400.
|
|
300
|
+
|
|
247
301
|
### Helper Methods
|
|
248
302
|
|
|
249
303
|
```python
|
|
@@ -344,6 +398,30 @@ boards = client.get_pinterest_boards("my-profile")
|
|
|
344
398
|
- `subreddit` - Subreddit name (without r/)
|
|
345
399
|
- `flair_id` - Flair template ID
|
|
346
400
|
|
|
401
|
+
### Google Business
|
|
402
|
+
- `gbp_location_id` - Location, e.g. `"accounts/123/locations/456"` (list them with `get_google_business_locations`). Required when the account has more than one location; the API only auto-selects when exactly one exists.
|
|
403
|
+
- `gbp_post_type` - `MEDIA`, `PHOTO` or `GALLERY` to publish into the location's photo gallery instead of creating a Local Post. Any other value, or omitting it, keeps the Local Post behaviour.
|
|
404
|
+
- `gbp_media_category` - Gallery category, default `ADDITIONAL`. One of `COVER`, `PROFILE`, `LOGO`, `EXTERIOR`, `INTERIOR`, `PRODUCT`, `AT_WORK`, `FOOD_AND_DRINK`, `MENU`, `COMMON_AREA`, `ROOMS`, `TEAMS`, `ADDITIONAL`.
|
|
405
|
+
- `gbp_topic_type` - STANDARD, EVENT or OFFER
|
|
406
|
+
- `gbp_media_url` / `gbp_media_format` - Media attached to the post
|
|
407
|
+
- `gbp_cta_type` / `gbp_cta_url` - Call-to-action button
|
|
408
|
+
- `gbp_event_title` / `gbp_event_start_date` / `gbp_event_start_time` / `gbp_event_end_date` / `gbp_event_end_time` - Used with `gbp_topic_type="EVENT"`
|
|
409
|
+
- `gbp_offer_coupon` / `gbp_offer_redeem_url` / `gbp_offer_terms` - Used with `gbp_topic_type="OFFER"`
|
|
410
|
+
|
|
411
|
+
```python
|
|
412
|
+
locations = client.get_google_business_locations("my-profile")
|
|
413
|
+
|
|
414
|
+
# Publish a photo straight into the location's gallery
|
|
415
|
+
client.upload_photos(
|
|
416
|
+
["storefront.jpg"],
|
|
417
|
+
user="my-profile",
|
|
418
|
+
platforms=["google_business"],
|
|
419
|
+
gbp_location_id=locations["locations"][0]["name"],
|
|
420
|
+
gbp_post_type="GALLERY",
|
|
421
|
+
gbp_media_category="EXTERIOR",
|
|
422
|
+
)
|
|
423
|
+
```
|
|
424
|
+
|
|
347
425
|
## Common Options
|
|
348
426
|
|
|
349
427
|
These options work across all upload methods:
|
|
@@ -360,9 +438,6 @@ These options work across all upload methods:
|
|
|
360
438
|
| `add_to_queue` | Add to posting queue |
|
|
361
439
|
| `max_posts_per_slot` | Max posts per queue slot (overrides profile setting) |
|
|
362
440
|
| `async_upload` | Process asynchronously (default: True) |
|
|
363
|
-
| `autogenerate` | If True, AI generates native per-platform title/description from the media and fills any field left empty |
|
|
364
|
-
| `autogenerate_title` / `autogenerate_description` | Generate only the title or only the description (bool) |
|
|
365
|
-
| `autogenerate_language` | Force the output language (ISO code); omit to auto-detect from the media |
|
|
366
441
|
|
|
367
442
|
## Error Handling
|
|
368
443
|
|
|
@@ -173,9 +173,16 @@ jwt = client.generate_jwt(
|
|
|
173
173
|
redirect_url="https://yourapp.com/callback",
|
|
174
174
|
platforms=["tiktok", "instagram"],
|
|
175
175
|
# Optional: force the connection page language for this profile.
|
|
176
|
-
# Supported: "en", "es", "de", "fr", "pt". When omitted, the page
|
|
176
|
+
# Supported: "en", "es", "de", "fr", "pt", "pl", "tr". When omitted, the page
|
|
177
177
|
# auto-detects the visitor's browser language and falls back to English.
|
|
178
178
|
language="es",
|
|
179
|
+
# Optional: override individual connection-page strings. Flat dict of i18n
|
|
180
|
+
# dot-path keys to strings. Max 100 entries, keys ^[a-zA-Z0-9_.]+$, values
|
|
181
|
+
# up to 300 chars. Echoed back in the "profile" object of validate_jwt.
|
|
182
|
+
ui_labels={
|
|
183
|
+
"connect.title": "Link your accounts",
|
|
184
|
+
"connect.subtitle": "Publish everywhere from one place",
|
|
185
|
+
},
|
|
179
186
|
)
|
|
180
187
|
```
|
|
181
188
|
|
|
@@ -187,6 +194,35 @@ analytics = client.get_analytics(
|
|
|
187
194
|
platforms=["instagram", "tiktok"],
|
|
188
195
|
)
|
|
189
196
|
print(analytics)
|
|
197
|
+
|
|
198
|
+
# Instagram returns two audience breakdowns with the same shape
|
|
199
|
+
# ("age", "gender", "country", "city"):
|
|
200
|
+
print(analytics["analytics"]["instagram"]["follower_demographics"])
|
|
201
|
+
print(analytics["analytics"]["instagram"]["engaged_audience_demographics"])
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Cached Post Analytics
|
|
205
|
+
|
|
206
|
+
Replays per-post metrics already fetched, instead of calling the platforms again. Only contains posts previously fetched through a live per-post endpoint; there is no background refresh, so captured_at is the last time that post was read live. Not subject to the live
|
|
207
|
+
calls, so it is not subject to the live post-analytics rate limit
|
|
208
|
+
(100 requests / 5 minutes). Use it to page through a profile's post history.
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
cursor = None
|
|
212
|
+
while True:
|
|
213
|
+
page = client.get_cached_post_analytics(
|
|
214
|
+
"my-profile",
|
|
215
|
+
platform="youtube", # optional: instagram, tiktok, youtube, facebook, linkedin, threads, pinterest, reddit
|
|
216
|
+
limit=50, # default 50, max 200
|
|
217
|
+
since="2026-06-01", # defaults to 30 days ago
|
|
218
|
+
until="2026-07-01", # defaults to today
|
|
219
|
+
cursor=cursor,
|
|
220
|
+
)
|
|
221
|
+
for post in page["posts"]:
|
|
222
|
+
print(post["platform"], post["post_id"], post["metrics"])
|
|
223
|
+
cursor = page["next_cursor"]
|
|
224
|
+
if not cursor:
|
|
225
|
+
break
|
|
190
226
|
```
|
|
191
227
|
|
|
192
228
|
### Get Media
|
|
@@ -206,6 +242,24 @@ media = client.get_media("linkedin", "my-profile", page_urn="me")
|
|
|
206
242
|
media = client.get_media("linkedin", "my-profile", page_urn="12345")
|
|
207
243
|
```
|
|
208
244
|
|
|
245
|
+
The response carries a `pagination` object — `{"limit": ..., "next_cursor": ...,
|
|
246
|
+
"has_more": ...}`, with `next_cursor` `None` and `has_more` `False` on the last
|
|
247
|
+
page:
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
cursor = None
|
|
251
|
+
while True:
|
|
252
|
+
page = client.get_media("instagram", "my-profile", limit=50, cursor=cursor)
|
|
253
|
+
print(len(page["media"]))
|
|
254
|
+
cursor = page["pagination"]["next_cursor"]
|
|
255
|
+
if not cursor:
|
|
256
|
+
break
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
`limit` defaults to 25 and is clamped to 1-100, with per-platform caps of 20 for
|
|
260
|
+
TikTok and 50 for YouTube. **LinkedIn, Discord and Telegram do not support
|
|
261
|
+
cursors** — they accept `limit` only, and passing a `cursor` returns HTTP 400.
|
|
262
|
+
|
|
209
263
|
### Helper Methods
|
|
210
264
|
|
|
211
265
|
```python
|
|
@@ -306,6 +360,30 @@ boards = client.get_pinterest_boards("my-profile")
|
|
|
306
360
|
- `subreddit` - Subreddit name (without r/)
|
|
307
361
|
- `flair_id` - Flair template ID
|
|
308
362
|
|
|
363
|
+
### Google Business
|
|
364
|
+
- `gbp_location_id` - Location, e.g. `"accounts/123/locations/456"` (list them with `get_google_business_locations`). Required when the account has more than one location; the API only auto-selects when exactly one exists.
|
|
365
|
+
- `gbp_post_type` - `MEDIA`, `PHOTO` or `GALLERY` to publish into the location's photo gallery instead of creating a Local Post. Any other value, or omitting it, keeps the Local Post behaviour.
|
|
366
|
+
- `gbp_media_category` - Gallery category, default `ADDITIONAL`. One of `COVER`, `PROFILE`, `LOGO`, `EXTERIOR`, `INTERIOR`, `PRODUCT`, `AT_WORK`, `FOOD_AND_DRINK`, `MENU`, `COMMON_AREA`, `ROOMS`, `TEAMS`, `ADDITIONAL`.
|
|
367
|
+
- `gbp_topic_type` - STANDARD, EVENT or OFFER
|
|
368
|
+
- `gbp_media_url` / `gbp_media_format` - Media attached to the post
|
|
369
|
+
- `gbp_cta_type` / `gbp_cta_url` - Call-to-action button
|
|
370
|
+
- `gbp_event_title` / `gbp_event_start_date` / `gbp_event_start_time` / `gbp_event_end_date` / `gbp_event_end_time` - Used with `gbp_topic_type="EVENT"`
|
|
371
|
+
- `gbp_offer_coupon` / `gbp_offer_redeem_url` / `gbp_offer_terms` - Used with `gbp_topic_type="OFFER"`
|
|
372
|
+
|
|
373
|
+
```python
|
|
374
|
+
locations = client.get_google_business_locations("my-profile")
|
|
375
|
+
|
|
376
|
+
# Publish a photo straight into the location's gallery
|
|
377
|
+
client.upload_photos(
|
|
378
|
+
["storefront.jpg"],
|
|
379
|
+
user="my-profile",
|
|
380
|
+
platforms=["google_business"],
|
|
381
|
+
gbp_location_id=locations["locations"][0]["name"],
|
|
382
|
+
gbp_post_type="GALLERY",
|
|
383
|
+
gbp_media_category="EXTERIOR",
|
|
384
|
+
)
|
|
385
|
+
```
|
|
386
|
+
|
|
309
387
|
## Common Options
|
|
310
388
|
|
|
311
389
|
These options work across all upload methods:
|
|
@@ -322,9 +400,6 @@ These options work across all upload methods:
|
|
|
322
400
|
| `add_to_queue` | Add to posting queue |
|
|
323
401
|
| `max_posts_per_slot` | Max posts per queue slot (overrides profile setting) |
|
|
324
402
|
| `async_upload` | Process asynchronously (default: True) |
|
|
325
|
-
| `autogenerate` | If True, AI generates native per-platform title/description from the media and fills any field left empty |
|
|
326
|
-
| `autogenerate_title` / `autogenerate_description` | Generate only the title or only the description (bool) |
|
|
327
|
-
| `autogenerate_language` | Force the output language (ISO code); omit to auto-detect from the media |
|
|
328
403
|
|
|
329
404
|
## Error Handling
|
|
330
405
|
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="upload-post",
|
|
8
|
-
version="2.
|
|
8
|
+
version="2.9.0",
|
|
9
9
|
author="Upload-Post",
|
|
10
10
|
author_email="hi@img2html.com",
|
|
11
11
|
description="Cross-platform social media upload for TikTok, Instagram, YouTube, LinkedIn, Facebook, Pinterest, Threads, Reddit, Bluesky, Discord, Telegram, and X (Twitter)",
|
|
@@ -129,14 +129,6 @@ class UploadPostClient:
|
|
|
129
129
|
if async_upload is not None:
|
|
130
130
|
data.append(("async_upload", str(async_upload).lower()))
|
|
131
131
|
# AI auto-generation of native per-platform copy from the media (fills blank fields)
|
|
132
|
-
if kwargs.get("autogenerate") is not None:
|
|
133
|
-
data.append(("autogenerate", str(kwargs["autogenerate"]).lower()))
|
|
134
|
-
if kwargs.get("autogenerate_title") is not None:
|
|
135
|
-
data.append(("autogenerate_title", str(kwargs["autogenerate_title"]).lower()))
|
|
136
|
-
if kwargs.get("autogenerate_description") is not None:
|
|
137
|
-
data.append(("autogenerate_description", str(kwargs["autogenerate_description"]).lower()))
|
|
138
|
-
if kwargs.get("autogenerate_language"):
|
|
139
|
-
data.append(("autogenerate_language", str(kwargs["autogenerate_language"])))
|
|
140
132
|
|
|
141
133
|
# Platform-specific title overrides
|
|
142
134
|
title_overrides = [
|
|
@@ -408,6 +400,49 @@ class UploadPostClient:
|
|
|
408
400
|
if reddit_link:
|
|
409
401
|
data.append(("reddit_link_url", reddit_link))
|
|
410
402
|
|
|
403
|
+
def _add_google_business_params(self, data: List[tuple], **kwargs):
|
|
404
|
+
"""Add Google Business Profile parameters.
|
|
405
|
+
|
|
406
|
+
``gbp_location_id`` is required for accounts with more than one location;
|
|
407
|
+
the API only auto-selects when exactly one location exists.
|
|
408
|
+
|
|
409
|
+
``gbp_post_type`` switches the call from the default Local Post to a
|
|
410
|
+
gallery photo upload (MEDIA / PHOTO / GALLERY). Any other value, or
|
|
411
|
+
omitting it, keeps the Local Post behaviour.
|
|
412
|
+
"""
|
|
413
|
+
if kwargs.get("gbp_location_id"):
|
|
414
|
+
data.append(("gbp_location_id", kwargs["gbp_location_id"]))
|
|
415
|
+
if kwargs.get("gbp_post_type"):
|
|
416
|
+
data.append(("gbp_post_type", str(kwargs["gbp_post_type"]).upper()))
|
|
417
|
+
if kwargs.get("gbp_media_category"):
|
|
418
|
+
data.append(("gbp_media_category", str(kwargs["gbp_media_category"]).upper()))
|
|
419
|
+
if kwargs.get("gbp_topic_type"):
|
|
420
|
+
data.append(("gbp_topic_type", str(kwargs["gbp_topic_type"]).upper()))
|
|
421
|
+
if kwargs.get("gbp_media_url"):
|
|
422
|
+
data.append(("gbp_media_url", kwargs["gbp_media_url"]))
|
|
423
|
+
if kwargs.get("gbp_media_format"):
|
|
424
|
+
data.append(("gbp_media_format", str(kwargs["gbp_media_format"]).upper()))
|
|
425
|
+
if kwargs.get("gbp_cta_type"):
|
|
426
|
+
data.append(("gbp_cta_type", str(kwargs["gbp_cta_type"]).upper()))
|
|
427
|
+
if kwargs.get("gbp_cta_url"):
|
|
428
|
+
data.append(("gbp_cta_url", kwargs["gbp_cta_url"]))
|
|
429
|
+
if kwargs.get("gbp_event_title"):
|
|
430
|
+
data.append(("gbp_event_title", kwargs["gbp_event_title"]))
|
|
431
|
+
if kwargs.get("gbp_event_start_date"):
|
|
432
|
+
data.append(("gbp_event_start_date", kwargs["gbp_event_start_date"]))
|
|
433
|
+
if kwargs.get("gbp_event_start_time"):
|
|
434
|
+
data.append(("gbp_event_start_time", kwargs["gbp_event_start_time"]))
|
|
435
|
+
if kwargs.get("gbp_event_end_date"):
|
|
436
|
+
data.append(("gbp_event_end_date", kwargs["gbp_event_end_date"]))
|
|
437
|
+
if kwargs.get("gbp_event_end_time"):
|
|
438
|
+
data.append(("gbp_event_end_time", kwargs["gbp_event_end_time"]))
|
|
439
|
+
if kwargs.get("gbp_offer_coupon"):
|
|
440
|
+
data.append(("gbp_offer_coupon", kwargs["gbp_offer_coupon"]))
|
|
441
|
+
if kwargs.get("gbp_offer_redeem_url"):
|
|
442
|
+
data.append(("gbp_offer_redeem_url", kwargs["gbp_offer_redeem_url"]))
|
|
443
|
+
if kwargs.get("gbp_offer_terms"):
|
|
444
|
+
data.append(("gbp_offer_terms", kwargs["gbp_offer_terms"]))
|
|
445
|
+
|
|
411
446
|
def upload_video(
|
|
412
447
|
self,
|
|
413
448
|
video_path: Union[str, Path],
|
|
@@ -426,7 +461,7 @@ class UploadPostClient:
|
|
|
426
461
|
user: User identifier (profile name).
|
|
427
462
|
platforms: Target platforms. Supported: tiktok, instagram, youtube,
|
|
428
463
|
linkedin, facebook, pinterest, threads, bluesky, discord, telegram, x,
|
|
429
|
-
mastodon, wordpress
|
|
464
|
+
google_business, mastodon, wordpress
|
|
430
465
|
|
|
431
466
|
Keyword Args:
|
|
432
467
|
description: Video description
|
|
@@ -435,10 +470,7 @@ class UploadPostClient:
|
|
|
435
470
|
timezone: Timezone for scheduled date (e.g., "Europe/Madrid")
|
|
436
471
|
add_to_queue: Add to posting queue
|
|
437
472
|
async_upload: Process asynchronously (default: True)
|
|
438
|
-
autogenerate: If True, the server uses AI to generate native per-platform
|
|
439
473
|
title/description from the media and fills any platform field
|
|
440
|
-
left empty. Also: autogenerate_title / autogenerate_description
|
|
441
|
-
(bool) for granularity, autogenerate_language (ISO code) to force
|
|
442
474
|
the language (omit to auto-detect from the media).
|
|
443
475
|
|
|
444
476
|
TikTok:
|
|
@@ -515,6 +547,22 @@ class UploadPostClient:
|
|
|
515
547
|
threads_long_text_as_post: Post long text as single post
|
|
516
548
|
threads_topic_tag: Topic tag for the post (1-50 chars, no periods or ampersands)
|
|
517
549
|
|
|
550
|
+
Google Business:
|
|
551
|
+
gbp_location_id: Location, e.g. "accounts/123/locations/456". Required
|
|
552
|
+
when the account has more than one location.
|
|
553
|
+
gbp_post_type: MEDIA, PHOTO or GALLERY to publish into the location's
|
|
554
|
+
gallery instead of creating a Local Post. Any other value, or
|
|
555
|
+
omitting it, keeps the Local Post behaviour.
|
|
556
|
+
gbp_media_category: Gallery category (default ADDITIONAL). One of
|
|
557
|
+
COVER, PROFILE, LOGO, EXTERIOR, INTERIOR, PRODUCT, AT_WORK,
|
|
558
|
+
FOOD_AND_DRINK, MENU, COMMON_AREA, ROOMS, TEAMS, ADDITIONAL.
|
|
559
|
+
gbp_topic_type: STANDARD, EVENT or OFFER
|
|
560
|
+
gbp_media_url / gbp_media_format: Media attached to the post
|
|
561
|
+
gbp_cta_type / gbp_cta_url: Call-to-action button
|
|
562
|
+
gbp_event_title / gbp_event_start_date / gbp_event_start_time /
|
|
563
|
+
gbp_event_end_date / gbp_event_end_time: Used with EVENT
|
|
564
|
+
gbp_offer_coupon / gbp_offer_redeem_url / gbp_offer_terms: Used with OFFER
|
|
565
|
+
|
|
518
566
|
Returns:
|
|
519
567
|
API response with request_id for async uploads.
|
|
520
568
|
|
|
@@ -554,7 +602,9 @@ class UploadPostClient:
|
|
|
554
602
|
self._add_x_params(data, is_text=False, **kwargs)
|
|
555
603
|
if "threads" in platforms:
|
|
556
604
|
self._add_threads_params(data, **kwargs)
|
|
557
|
-
|
|
605
|
+
if "google_business" in platforms:
|
|
606
|
+
self._add_google_business_params(data, **kwargs)
|
|
607
|
+
|
|
558
608
|
return self._request("/upload", "POST", data=data, files=files if files else None)
|
|
559
609
|
|
|
560
610
|
finally:
|
|
@@ -579,7 +629,7 @@ class UploadPostClient:
|
|
|
579
629
|
user: User identifier (profile name).
|
|
580
630
|
platforms: Target platforms. Supported: tiktok, instagram, linkedin,
|
|
581
631
|
facebook, pinterest, threads, reddit, bluesky, discord, telegram, x,
|
|
582
|
-
mastodon, lemmy, wordpress
|
|
632
|
+
google_business, mastodon, lemmy, wordpress
|
|
583
633
|
|
|
584
634
|
Keyword Args:
|
|
585
635
|
description: Photo description
|
|
@@ -638,6 +688,18 @@ class UploadPostClient:
|
|
|
638
688
|
subreddit: Subreddit name (without r/)
|
|
639
689
|
flair_id: Flair template ID
|
|
640
690
|
|
|
691
|
+
Google Business:
|
|
692
|
+
gbp_location_id: Location, e.g. "accounts/123/locations/456". Required
|
|
693
|
+
when the account has more than one location.
|
|
694
|
+
gbp_post_type: MEDIA, PHOTO or GALLERY to publish the photo into the
|
|
695
|
+
location's gallery instead of creating a Local Post. Any other
|
|
696
|
+
value, or omitting it, keeps the Local Post behaviour.
|
|
697
|
+
gbp_media_category: Gallery category (default ADDITIONAL). One of
|
|
698
|
+
COVER, PROFILE, LOGO, EXTERIOR, INTERIOR, PRODUCT, AT_WORK,
|
|
699
|
+
FOOD_AND_DRINK, MENU, COMMON_AREA, ROOMS, TEAMS, ADDITIONAL.
|
|
700
|
+
gbp_topic_type: STANDARD, EVENT or OFFER
|
|
701
|
+
gbp_cta_type / gbp_cta_url: Call-to-action button
|
|
702
|
+
|
|
641
703
|
first_comment_media: List of file paths to attach as images in
|
|
642
704
|
the first comment. Supported on Reddit and X.
|
|
643
705
|
|
|
@@ -682,6 +744,8 @@ class UploadPostClient:
|
|
|
682
744
|
self._add_threads_params(data, **kwargs)
|
|
683
745
|
if "reddit" in platforms:
|
|
684
746
|
self._add_reddit_params(data, **kwargs)
|
|
747
|
+
if "google_business" in platforms:
|
|
748
|
+
self._add_google_business_params(data, **kwargs)
|
|
685
749
|
|
|
686
750
|
first_comment_media = kwargs.get("first_comment_media")
|
|
687
751
|
if first_comment_media:
|
|
@@ -713,8 +777,9 @@ class UploadPostClient:
|
|
|
713
777
|
title: Text content for the post.
|
|
714
778
|
user: User identifier (profile name).
|
|
715
779
|
platforms: Target platforms. Supported: x, linkedin, facebook,
|
|
716
|
-
threads, reddit, bluesky, discord, telegram,
|
|
717
|
-
nostr, lemmy, devto, hashnode, wordpress,
|
|
780
|
+
threads, reddit, bluesky, discord, telegram, google_business,
|
|
781
|
+
slack, mastodon, nostr, lemmy, devto, hashnode, wordpress,
|
|
782
|
+
whop, listmonk
|
|
718
783
|
|
|
719
784
|
Keyword Args:
|
|
720
785
|
first_comment: First comment to post
|
|
@@ -757,6 +822,15 @@ class UploadPostClient:
|
|
|
757
822
|
(kind: "link") instead of a text post. Overrides `link_url`
|
|
758
823
|
for Reddit.
|
|
759
824
|
|
|
825
|
+
Google Business:
|
|
826
|
+
gbp_location_id: Location, e.g. "accounts/123/locations/456". Required
|
|
827
|
+
when the account has more than one location.
|
|
828
|
+
gbp_topic_type: STANDARD, EVENT or OFFER
|
|
829
|
+
gbp_cta_type / gbp_cta_url: Call-to-action button
|
|
830
|
+
gbp_event_title / gbp_event_start_date / gbp_event_start_time /
|
|
831
|
+
gbp_event_end_date / gbp_event_end_time: Used with EVENT
|
|
832
|
+
gbp_offer_coupon / gbp_offer_redeem_url / gbp_offer_terms: Used with OFFER
|
|
833
|
+
|
|
760
834
|
first_comment_media: List of file paths to attach as images in
|
|
761
835
|
the first comment. Supported on Reddit and X.
|
|
762
836
|
|
|
@@ -789,6 +863,8 @@ class UploadPostClient:
|
|
|
789
863
|
bluesky_link = kwargs.get("bluesky_link_url")
|
|
790
864
|
if bluesky_link:
|
|
791
865
|
data.append(("bluesky_link_url", bluesky_link))
|
|
866
|
+
if "google_business" in platforms:
|
|
867
|
+
self._add_google_business_params(data, **kwargs)
|
|
792
868
|
|
|
793
869
|
first_comment_media = kwargs.get("first_comment_media")
|
|
794
870
|
opened_files: List = []
|
|
@@ -984,7 +1060,11 @@ class UploadPostClient:
|
|
|
984
1060
|
|
|
985
1061
|
Returns:
|
|
986
1062
|
Analytics data per platform. For Instagram, the response includes both
|
|
987
|
-
'views' (official Instagram metric) and 'impressions' (backwards-compatible alias)
|
|
1063
|
+
'views' (official Instagram metric) and 'impressions' (backwards-compatible alias),
|
|
1064
|
+
plus two audience breakdowns with the same shape ('age', 'gender',
|
|
1065
|
+
'country', 'city'): 'follower_demographics' for the account's followers
|
|
1066
|
+
and 'engaged_audience_demographics' for the accounts that engaged with
|
|
1067
|
+
its content.
|
|
988
1068
|
"""
|
|
989
1069
|
params = {}
|
|
990
1070
|
if platforms:
|
|
@@ -1070,6 +1150,49 @@ class UploadPostClient:
|
|
|
1070
1150
|
}
|
|
1071
1151
|
return self._request("/uploadposts/post-analytics", "GET", params=params)
|
|
1072
1152
|
|
|
1153
|
+
def get_cached_post_analytics(
|
|
1154
|
+
self,
|
|
1155
|
+
user: str,
|
|
1156
|
+
platform: Optional[str] = None,
|
|
1157
|
+
limit: Optional[int] = None,
|
|
1158
|
+
cursor: Optional[str] = None,
|
|
1159
|
+
since: Optional[str] = None,
|
|
1160
|
+
until: Optional[str] = None,
|
|
1161
|
+
) -> Dict:
|
|
1162
|
+
"""
|
|
1163
|
+
Replay per-post metrics already fetched, instead of querying
|
|
1164
|
+
the platforms live.
|
|
1165
|
+
|
|
1166
|
+
Because it never calls the platform APIs it is not subject to the live
|
|
1167
|
+
post-analytics rate limit (100 requests / 5 minutes), which makes it the
|
|
1168
|
+
right method for paging through a profile's whole post history.
|
|
1169
|
+
|
|
1170
|
+
Args:
|
|
1171
|
+
user: Profile username.
|
|
1172
|
+
platform: Filter by platform (instagram, tiktok, youtube, facebook,
|
|
1173
|
+
linkedin, threads, pinterest, reddit).
|
|
1174
|
+
limit: Posts per page. Defaults to 50, max 200.
|
|
1175
|
+
cursor: Opaque cursor from a previous response's ``next_cursor``.
|
|
1176
|
+
since: Start date in YYYY-MM-DD format (defaults to 30 days ago).
|
|
1177
|
+
until: End date in YYYY-MM-DD format (defaults to today).
|
|
1178
|
+
|
|
1179
|
+
Returns:
|
|
1180
|
+
Cached post metrics with ``source: "snapshot_cache"``, plus
|
|
1181
|
+
``next_cursor`` and ``has_more`` for pagination.
|
|
1182
|
+
"""
|
|
1183
|
+
params: Dict[str, Any] = {"user": user}
|
|
1184
|
+
if platform:
|
|
1185
|
+
params["platform"] = platform
|
|
1186
|
+
if limit is not None:
|
|
1187
|
+
params["limit"] = limit
|
|
1188
|
+
if cursor:
|
|
1189
|
+
params["cursor"] = cursor
|
|
1190
|
+
if since:
|
|
1191
|
+
params["since"] = since
|
|
1192
|
+
if until:
|
|
1193
|
+
params["until"] = until
|
|
1194
|
+
return self._request("/uploadposts/post-analytics/cached", "GET", params=params)
|
|
1195
|
+
|
|
1073
1196
|
def get_platform_metrics(self) -> Dict:
|
|
1074
1197
|
"""
|
|
1075
1198
|
Get available metrics configuration for all supported platforms.
|
|
@@ -1084,6 +1207,8 @@ class UploadPostClient:
|
|
|
1084
1207
|
platform: str,
|
|
1085
1208
|
user: str,
|
|
1086
1209
|
page_urn: Optional[str] = None,
|
|
1210
|
+
limit: Optional[int] = None,
|
|
1211
|
+
cursor: Optional[str] = None,
|
|
1087
1212
|
) -> Dict:
|
|
1088
1213
|
"""
|
|
1089
1214
|
Retrieve recent media from a connected social account.
|
|
@@ -1094,13 +1219,24 @@ class UploadPostClient:
|
|
|
1094
1219
|
user: Profile username.
|
|
1095
1220
|
page_urn: LinkedIn only. Numeric org ID, full URN, or ``"me"`` to
|
|
1096
1221
|
force the personal profile of an org-admin account.
|
|
1222
|
+
limit: Items per page. Defaults to 25 and is clamped to 1-100.
|
|
1223
|
+
Per-platform caps: TikTok 20, YouTube 50, everything else 100.
|
|
1224
|
+
cursor: Opaque cursor from a previous response's
|
|
1225
|
+
``pagination["next_cursor"]``. LinkedIn, Discord and Telegram do
|
|
1226
|
+
not support cursors: passing one there returns HTTP 400.
|
|
1097
1227
|
|
|
1098
1228
|
Returns:
|
|
1099
|
-
``{"success": True, "media": [...]
|
|
1229
|
+
``{"success": True, "media": [...], "pagination": {"limit": 50,
|
|
1230
|
+
"next_cursor": "QVFI...", "has_more": True}}``. The last page has
|
|
1231
|
+
``next_cursor`` ``None`` and ``has_more`` ``False``.
|
|
1100
1232
|
"""
|
|
1101
|
-
params: Dict[str,
|
|
1233
|
+
params: Dict[str, Any] = {"platform": platform, "user": user}
|
|
1102
1234
|
if page_urn:
|
|
1103
1235
|
params["page_urn"] = page_urn
|
|
1236
|
+
if limit is not None:
|
|
1237
|
+
params["limit"] = limit
|
|
1238
|
+
if cursor:
|
|
1239
|
+
params["cursor"] = cursor
|
|
1104
1240
|
return self._request("/uploadposts/media", "GET", params=params)
|
|
1105
1241
|
|
|
1106
1242
|
# ==================== Scheduled Posts ====================
|
|
@@ -1196,7 +1332,8 @@ class UploadPostClient:
|
|
|
1196
1332
|
readonly_calendar: Optional[bool] = None,
|
|
1197
1333
|
connect_title: Optional[str] = None,
|
|
1198
1334
|
connect_description: Optional[str] = None,
|
|
1199
|
-
language: Optional[str] = None
|
|
1335
|
+
language: Optional[str] = None,
|
|
1336
|
+
ui_labels: Optional[Dict[str, str]] = None
|
|
1200
1337
|
) -> Dict:
|
|
1201
1338
|
"""
|
|
1202
1339
|
Generate a JWT for platform integration.
|
|
@@ -1213,8 +1350,13 @@ class UploadPostClient:
|
|
|
1213
1350
|
connect_title: Custom title for the connection page.
|
|
1214
1351
|
connect_description: Custom description for the connection page.
|
|
1215
1352
|
language: Force the connection page language for this profile.
|
|
1216
|
-
Supported: 'en', 'es', 'de', 'fr', 'pt'. When omitted,
|
|
1217
|
-
auto-detects the visitor's browser language and falls back
|
|
1353
|
+
Supported: 'en', 'es', 'de', 'fr', 'pt', 'pl', 'tr'. When omitted,
|
|
1354
|
+
the page auto-detects the visitor's browser language and falls back
|
|
1355
|
+
to English.
|
|
1356
|
+
ui_labels: Flat mapping of i18n dot-path keys to replacement strings,
|
|
1357
|
+
e.g. {"connect.title": "Link your accounts"}. Max 100 entries; keys
|
|
1358
|
+
must match ^[a-zA-Z0-9_.]+$ and values are strings of up to 300
|
|
1359
|
+
characters. Echoed back in the 'profile' object of validate_jwt.
|
|
1218
1360
|
|
|
1219
1361
|
Returns:
|
|
1220
1362
|
JWT and connection URL.
|
|
@@ -1238,6 +1380,8 @@ class UploadPostClient:
|
|
|
1238
1380
|
body["connect_description"] = connect_description
|
|
1239
1381
|
if language:
|
|
1240
1382
|
body["language"] = language
|
|
1383
|
+
if ui_labels:
|
|
1384
|
+
body["ui_labels"] = ui_labels
|
|
1241
1385
|
return self._request("/uploadposts/users/generate-jwt", "POST", json_data=body)
|
|
1242
1386
|
|
|
1243
1387
|
def validate_jwt(self, jwt: str) -> Dict:
|
|
@@ -1248,7 +1392,9 @@ class UploadPostClient:
|
|
|
1248
1392
|
jwt: JWT token to validate.
|
|
1249
1393
|
|
|
1250
1394
|
Returns:
|
|
1251
|
-
Validation result.
|
|
1395
|
+
Validation result. The 'profile' object echoes back the connection
|
|
1396
|
+
page settings, including 'language' and any 'ui_labels' sent to
|
|
1397
|
+
generate_jwt.
|
|
1252
1398
|
"""
|
|
1253
1399
|
return self._request("/uploadposts/users/validate-jwt", "POST", json_data={"jwt": jwt})
|
|
1254
1400
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: upload-post
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.0
|
|
4
4
|
Summary: Cross-platform social media upload for TikTok, Instagram, YouTube, LinkedIn, Facebook, Pinterest, Threads, Reddit, Bluesky, Discord, Telegram, and X (Twitter)
|
|
5
5
|
Home-page: https://www.upload-post.com/
|
|
6
6
|
Author: Upload-Post
|
|
@@ -211,9 +211,16 @@ jwt = client.generate_jwt(
|
|
|
211
211
|
redirect_url="https://yourapp.com/callback",
|
|
212
212
|
platforms=["tiktok", "instagram"],
|
|
213
213
|
# Optional: force the connection page language for this profile.
|
|
214
|
-
# Supported: "en", "es", "de", "fr", "pt". When omitted, the page
|
|
214
|
+
# Supported: "en", "es", "de", "fr", "pt", "pl", "tr". When omitted, the page
|
|
215
215
|
# auto-detects the visitor's browser language and falls back to English.
|
|
216
216
|
language="es",
|
|
217
|
+
# Optional: override individual connection-page strings. Flat dict of i18n
|
|
218
|
+
# dot-path keys to strings. Max 100 entries, keys ^[a-zA-Z0-9_.]+$, values
|
|
219
|
+
# up to 300 chars. Echoed back in the "profile" object of validate_jwt.
|
|
220
|
+
ui_labels={
|
|
221
|
+
"connect.title": "Link your accounts",
|
|
222
|
+
"connect.subtitle": "Publish everywhere from one place",
|
|
223
|
+
},
|
|
217
224
|
)
|
|
218
225
|
```
|
|
219
226
|
|
|
@@ -225,6 +232,35 @@ analytics = client.get_analytics(
|
|
|
225
232
|
platforms=["instagram", "tiktok"],
|
|
226
233
|
)
|
|
227
234
|
print(analytics)
|
|
235
|
+
|
|
236
|
+
# Instagram returns two audience breakdowns with the same shape
|
|
237
|
+
# ("age", "gender", "country", "city"):
|
|
238
|
+
print(analytics["analytics"]["instagram"]["follower_demographics"])
|
|
239
|
+
print(analytics["analytics"]["instagram"]["engaged_audience_demographics"])
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Cached Post Analytics
|
|
243
|
+
|
|
244
|
+
Replays per-post metrics already fetched, instead of calling the platforms again. Only contains posts previously fetched through a live per-post endpoint; there is no background refresh, so captured_at is the last time that post was read live. Not subject to the live
|
|
245
|
+
calls, so it is not subject to the live post-analytics rate limit
|
|
246
|
+
(100 requests / 5 minutes). Use it to page through a profile's post history.
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
cursor = None
|
|
250
|
+
while True:
|
|
251
|
+
page = client.get_cached_post_analytics(
|
|
252
|
+
"my-profile",
|
|
253
|
+
platform="youtube", # optional: instagram, tiktok, youtube, facebook, linkedin, threads, pinterest, reddit
|
|
254
|
+
limit=50, # default 50, max 200
|
|
255
|
+
since="2026-06-01", # defaults to 30 days ago
|
|
256
|
+
until="2026-07-01", # defaults to today
|
|
257
|
+
cursor=cursor,
|
|
258
|
+
)
|
|
259
|
+
for post in page["posts"]:
|
|
260
|
+
print(post["platform"], post["post_id"], post["metrics"])
|
|
261
|
+
cursor = page["next_cursor"]
|
|
262
|
+
if not cursor:
|
|
263
|
+
break
|
|
228
264
|
```
|
|
229
265
|
|
|
230
266
|
### Get Media
|
|
@@ -244,6 +280,24 @@ media = client.get_media("linkedin", "my-profile", page_urn="me")
|
|
|
244
280
|
media = client.get_media("linkedin", "my-profile", page_urn="12345")
|
|
245
281
|
```
|
|
246
282
|
|
|
283
|
+
The response carries a `pagination` object — `{"limit": ..., "next_cursor": ...,
|
|
284
|
+
"has_more": ...}`, with `next_cursor` `None` and `has_more` `False` on the last
|
|
285
|
+
page:
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
cursor = None
|
|
289
|
+
while True:
|
|
290
|
+
page = client.get_media("instagram", "my-profile", limit=50, cursor=cursor)
|
|
291
|
+
print(len(page["media"]))
|
|
292
|
+
cursor = page["pagination"]["next_cursor"]
|
|
293
|
+
if not cursor:
|
|
294
|
+
break
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
`limit` defaults to 25 and is clamped to 1-100, with per-platform caps of 20 for
|
|
298
|
+
TikTok and 50 for YouTube. **LinkedIn, Discord and Telegram do not support
|
|
299
|
+
cursors** — they accept `limit` only, and passing a `cursor` returns HTTP 400.
|
|
300
|
+
|
|
247
301
|
### Helper Methods
|
|
248
302
|
|
|
249
303
|
```python
|
|
@@ -344,6 +398,30 @@ boards = client.get_pinterest_boards("my-profile")
|
|
|
344
398
|
- `subreddit` - Subreddit name (without r/)
|
|
345
399
|
- `flair_id` - Flair template ID
|
|
346
400
|
|
|
401
|
+
### Google Business
|
|
402
|
+
- `gbp_location_id` - Location, e.g. `"accounts/123/locations/456"` (list them with `get_google_business_locations`). Required when the account has more than one location; the API only auto-selects when exactly one exists.
|
|
403
|
+
- `gbp_post_type` - `MEDIA`, `PHOTO` or `GALLERY` to publish into the location's photo gallery instead of creating a Local Post. Any other value, or omitting it, keeps the Local Post behaviour.
|
|
404
|
+
- `gbp_media_category` - Gallery category, default `ADDITIONAL`. One of `COVER`, `PROFILE`, `LOGO`, `EXTERIOR`, `INTERIOR`, `PRODUCT`, `AT_WORK`, `FOOD_AND_DRINK`, `MENU`, `COMMON_AREA`, `ROOMS`, `TEAMS`, `ADDITIONAL`.
|
|
405
|
+
- `gbp_topic_type` - STANDARD, EVENT or OFFER
|
|
406
|
+
- `gbp_media_url` / `gbp_media_format` - Media attached to the post
|
|
407
|
+
- `gbp_cta_type` / `gbp_cta_url` - Call-to-action button
|
|
408
|
+
- `gbp_event_title` / `gbp_event_start_date` / `gbp_event_start_time` / `gbp_event_end_date` / `gbp_event_end_time` - Used with `gbp_topic_type="EVENT"`
|
|
409
|
+
- `gbp_offer_coupon` / `gbp_offer_redeem_url` / `gbp_offer_terms` - Used with `gbp_topic_type="OFFER"`
|
|
410
|
+
|
|
411
|
+
```python
|
|
412
|
+
locations = client.get_google_business_locations("my-profile")
|
|
413
|
+
|
|
414
|
+
# Publish a photo straight into the location's gallery
|
|
415
|
+
client.upload_photos(
|
|
416
|
+
["storefront.jpg"],
|
|
417
|
+
user="my-profile",
|
|
418
|
+
platforms=["google_business"],
|
|
419
|
+
gbp_location_id=locations["locations"][0]["name"],
|
|
420
|
+
gbp_post_type="GALLERY",
|
|
421
|
+
gbp_media_category="EXTERIOR",
|
|
422
|
+
)
|
|
423
|
+
```
|
|
424
|
+
|
|
347
425
|
## Common Options
|
|
348
426
|
|
|
349
427
|
These options work across all upload methods:
|
|
@@ -360,9 +438,6 @@ These options work across all upload methods:
|
|
|
360
438
|
| `add_to_queue` | Add to posting queue |
|
|
361
439
|
| `max_posts_per_slot` | Max posts per queue slot (overrides profile setting) |
|
|
362
440
|
| `async_upload` | Process asynchronously (default: True) |
|
|
363
|
-
| `autogenerate` | If True, AI generates native per-platform title/description from the media and fills any field left empty |
|
|
364
|
-
| `autogenerate_title` / `autogenerate_description` | Generate only the title or only the description (bool) |
|
|
365
|
-
| `autogenerate_language` | Force the output language (ISO code); omit to auto-detect from the media |
|
|
366
441
|
|
|
367
442
|
## Error Handling
|
|
368
443
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|