Python Quickstart - TokPortal API
End-to-end Python requests workflow: create a TokPortal bundle, configure account and video fields, optionally upload media, publish, and read analytics.
Python Quickstart
This guide uses raw HTTP with requests. TokPortal also publishes a Node SDK and CLI; Python generated clients may exist locally, but the stable public Python path today is raw HTTP.
Reference: SDKs & CLI
1. Install
pip install requests
2. Configure auth
TokPortal uses the X-API-Key header.
BASE_URL = "https://app.tokportal.com/api/ext"
API_KEY = os.environ["TOKPORTAL_API_KEY"]
JSON_HEADERS = {
"X-API-Key": API_KEY,
"Content-Type": "application/json",
}
AUTH_HEADERS = {
"X-API-Key": API_KEY,
}
Reference: Authentication
3. Create a bundle
def create_bundle() -> str:
response = requests.post(
f"{BASE_URL}/bundles",
headers=JSON_HEADERS,
json={
"bundle_type": "account_and_videos",
"platform": "tiktok",
"country": "US",
"videos_quantity": 1,
"title": "Python quickstart",
"external_ref": "python-quickstart-001",
},
timeout=30,
)
response.raise_for_status()
payload = response.json()
print("credits charged:", payload.get("credits_charged"))
return payload["data"]["bundle_id"]
Reference: Create Bundle
4. Configure account
Publishing requires account configuration.
def configure_account(bundle_id: str) -> dict:
response = requests.put(
f"{BASE_URL}/bundles/{bundle_id}/account",
headers=JSON_HEADERS,
json={
"username": "python_demo_us",
"visible_name": "Python Demo",
"biography": "Daily product videos.",
"profile_picture_url": "https://cdn.example.com/profiles/python-demo.jpg",
},
timeout=30,
)
response.raise_for_status()
return response.json()
Reference: Account Configuration
5. Provide a video URL
If your video is already available at a public/direct URL, use that URL directly as video_url.
video_url = "https://cdn.example.com/videos/promo.mp4"
For local files, use the presigned upload flow. This is usually better for 50-100 MB videos because the binary uploads directly to storage instead of going through your automation tool.
def upload_video_presigned(bundle_id: str, filepath: str) -> str:
response = requests.post(
f"{BASE_URL}/upload/video",
headers=JSON_HEADERS,
json={
"filename": os.path.basename(filepath),
"content_type": "video/mp4",
"bundle_id": bundle_id,
},
timeout=30,
)
response.raise_for_status()
upload = response.json()["data"]
with open(filepath, "rb") as file_handle:
put_response = requests.put(
upload["upload_url"],
data=file_handle,
headers={"Content-Type": upload["content_type"]},
timeout=300,
)
put_response.raise_for_status()
return upload["public_url"]
Reference: Media Upload
6. Configure the video slot
def configure_video(bundle_id: str, video_url: str) -> dict:
response = requests.put(
f"{BASE_URL}/bundles/{bundle_id}/videos/1",
headers=JSON_HEADERS,
json={
"video_type": "video",
"description": "Built from Python #api",
"target_publish_date": "2026-06-05",
"video_url": video_url,
"external_ref": "python-video-001",
},
timeout=30,
)
response.raise_for_status()
return response.json()
Reference: Configure Videos
7. Publish
def publish_bundle(bundle_id: str) -> dict:
response = requests.post(
f"{BASE_URL}/bundles/{bundle_id}/publish",
headers=AUTH_HEADERS,
timeout=30,
)
response.raise_for_status()
return response.json()
If publishing fails, inspect readiness:
def get_publish_readiness(bundle_id: str) -> dict:
response = requests.get(
f"{BASE_URL}/bundles/{bundle_id}/publish-readiness",
headers=AUTH_HEADERS,
timeout=30,
)
response.raise_for_status()
return response.json()
Reference: Publish & Unpublish
8. Analytics
Analytics are available after the account is delivered and posts have data.
def get_account_analytics(account_id: str) -> dict:
response = requests.get(
f"{BASE_URL}/accounts/{account_id}/analytics",
headers=AUTH_HEADERS,
timeout=30,
)
response.raise_for_status()
return response.json()
Reference: Analytics
Full script
BASE_URL = "https://app.tokportal.com/api/ext"
API_KEY = os.environ["TOKPORTAL_API_KEY"]
JSON_HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"}
AUTH_HEADERS = {"X-API-Key": API_KEY}
def create_bundle() -> str:
response = requests.post(
f"{BASE_URL}/bundles",
headers=JSON_HEADERS,
json={
"bundle_type": "account_and_videos",
"platform": "tiktok",
"country": "US",
"videos_quantity": 1,
"title": "Python quickstart",
"external_ref": "python-quickstart-001",
},
timeout=30,
)
response.raise_for_status()
payload = response.json()
print("credits charged:", payload.get("credits_charged"))
return payload["data"]["bundle_id"]
def configure_account(bundle_id: str) -> None:
response = requests.put(
f"{BASE_URL}/bundles/{bundle_id}/account",
headers=JSON_HEADERS,
json={
"username": "python_demo_us",
"visible_name": "Python Demo",
"biography": "Daily product videos.",
"profile_picture_url": "https://cdn.example.com/profiles/python-demo.jpg",
},
timeout=30,
)
response.raise_for_status()
def upload_video_presigned(bundle_id: str, filepath: str) -> str:
response = requests.post(
f"{BASE_URL}/upload/video",
headers=JSON_HEADERS,
json={
"filename": os.path.basename(filepath),
"content_type": "video/mp4",
"bundle_id": bundle_id,
},
timeout=30,
)
response.raise_for_status()
upload = response.json()["data"]
with open(filepath, "rb") as file_handle:
put_response = requests.put(
upload["upload_url"],
data=file_handle,
headers={"Content-Type": upload["content_type"]},
timeout=300,
)
put_response.raise_for_status()
return upload["public_url"]
def configure_video(bundle_id: str, video_url: str) -> None:
response = requests.put(
f"{BASE_URL}/bundles/{bundle_id}/videos/1",
headers=JSON_HEADERS,
json={
"video_type": "video",
"description": "Built from Python #api",
"target_publish_date": "2026-06-05",
"video_url": video_url,
"external_ref": "python-video-001",
},
timeout=30,
)
response.raise_for_status()
def publish_bundle(bundle_id: str) -> dict:
response = requests.post(
f"{BASE_URL}/bundles/{bundle_id}/publish",
headers=AUTH_HEADERS,
timeout=30,
)
response.raise_for_status()
return response.json()
if __name__ == "__main__":
bundle_id = create_bundle()
configure_account(bundle_id)
local_video = os.environ.get("PROMO_VIDEO_PATH")
if local_video:
video_url = upload_video_presigned(bundle_id, local_video)
else:
video_url = os.environ["PROMO_VIDEO_URL"]
configure_video(bundle_id, video_url)
result = publish_bundle(bundle_id)
print(result)
Related docs: Create Bundle, Media Upload, Webhooks, MCP Server.