Skip to main content
Glama
tokportal

tokportal-mcp

Official
by tokportal

tokportal

PyPI Python license

TokPortal is the managed social infrastructure API: real TikTok, Instagram and YouTube accounts created, warmed and operated by human account managers in 16+ countries — exposed as a REST API and an MCP server. No OAuth per account, no 25-posts/day cap, no app review.

Docs https://developers.tokportal.com · API base https://app.tokportal.com/api/ext · OpenAPI https://developers.tokportal.com/openapi.json · MCP remote https://app.tokportal.com/api/ext/mcp · Get an API key https://app.tokportal.com/developer/api-keys?utm_source=pypi&utm_medium=readme&utm_campaign=tokportal-python · llms.txt https://developers.tokportal.com/llms.txt


tokportal is the official Python SDK for the TokPortal API (Python 3.9+, typed, standard library only). Every public operation is available as a resource method or through the generated request_operation map.

Install

pip install tokportal

30-second quickstart

import os
from tokportal import TokPortal

client = TokPortal(api_key=os.environ["TOKPORTAL_API_KEY"])

# 1. Create a bundle: a fresh managed TikTok account in the USA + 1 video slot.
#    Credits are debited now; the account manager is assigned at publish time.
bundle = client.bundles.create({
    "bundle_type": "account_and_videos",
    "platform": "tiktok",
    "country": "USA",
    "title": "US launch",
    "videos_quantity": 1,
})
bundle_id = bundle["data"]["id"]

# 2. Upload the video straight from disk -> public_url
upload = client.uploads.video_direct("./launch.mp4", bundle_id, content_type="video/mp4")

# 3. Configure the account profile and video slot 1, then publish
client.bundles.configure_account(bundle_id, {
    "username": "mybrand.us",
    "visible_name": "My Brand",
    "biography": "Official account",
})
client.bundles.configure_video(bundle_id, 1, {
    "video_type": "video",
    "video_url": upload["data"]["public_url"],
    "description": "Day 1 - launching in the US #launch",
    "target_publish_date": "2026-09-01",
})
client.bundles.publish(bundle_id)

# 4. Later (webhook `account.in_review` / `account.finalized`, or polling):
#    saved_account_id is the real delivered account -> read it back
current = client.bundles.get(bundle_id)["data"]
if current.get("saved_account_id"):
    account = client.accounts.get(current["saved_account_id"])["data"]
    print(account["username"], account["profile_url"])

Method names follow the generated resource map (bundles, uploads, accounts, analytics, webhooks). If a helper does not exist for an operation, use client.request_operation("<operationId>", path=..., query=..., body=...).

Full example

import os
from tokportal import TokPortal, TokPortalApiError

client = TokPortal(api_key=os.environ["TOKPORTAL_API_KEY"])

me = client.me()

bundle = client.bundles.create({
    "bundle_type": "account_and_videos",
    "country": "USA",
    "videos_quantity": 5,
})

csv = client.analytics.export_videos(account=["saved-account-id"])
image = client.uploads.image_from_url({
    "url": "https://cdn.example.com/photo.jpg",
    "bundle_id": bundle["data"]["id"],
})

print(me["data"]["email"], bundle["data"], csv, image["data"])

Direct multipart uploads use the same structured errors and idempotency support:

uploaded = client.uploads.video_direct(
    "./video.mp4",
    bundle["data"]["id"],
    content_type="video/mp4",
    idempotency_key="video-upload-123",
)

Manage TokPortal Coverage from the latest atomic quote. A zero-credit quote is valid and still requires an explicit reactivation call:

coverage = client.accounts.coverage("saved-account-id")
quote = coverage["data"]["reactivation_quote"]

if quote:
    client.accounts.reactivate_coverage(
        "saved-account-id",
        {
            "expected_credits": quote["credits"],
            "expected_current_period_end": quote["current_period_end"],
            "expected_lock_version": quote["lock_version"],
        },
        idempotency_key="coverage-reactivate-saved-account-id-v4",
    )

client.accounts.pause_coverage(
    "saved-account-id",
    idempotency_key="coverage-pause-saved-account-id-v4",
)

Credential reveal and verification-code access use the same irreversible two-step policy flow. First call without acceptance to receive HTTP 428 and error.details.policy_version; then show those terms to the account owner and retry with that exact version. The accepted request may debit credits and permanently detach the account. These secret-bearing responses are never stored for replay, so these helpers intentionally do not accept idempotency_key. After an uncertain transport result, reconcile the safe account state before deciding whether to call the endpoint again without a key:

If an accepted call returns HTTP 409 with CREDENTIAL_REVEAL_QUOTE_CHANGED, no charge or reveal occurred. Read the current policy and expected_credit_cost from error.details, show the new terms to the owner, obtain fresh consent, and retry with the new version. Never retry a 409 automatically.

try:
    client.accounts.reveal_credentials("saved-account-id")
except TokPortalApiError as error:
    if error.status_code != 428:
        raise

    policy_version = str(error.details["policy_version"])
    credentials = client.accounts.reveal_credentials(
        "saved-account-id",
        acceptance={
            "acknowledge_support_forfeit": True,
            "policy_version": policy_version,
        },
    )

The same no-replay rule applies to webhooks.create, uploads.image, uploads.video, and analytics.create_report because they return a signing secret, signed upload capability, or report access token. These helpers do not accept idempotency_key, and request_operation rejects one locally for all six sensitive operation IDs.

Discover and operate webhooks without dropping to raw HTTP:

catalog = client.webhooks.events()
endpoints = client.webhooks.list(event="bundle.published")
retry = client.webhooks.retry_delivery(endpoints["data"][0]["id"], "delivery-id")

Every OpenAPI operation is also reachable through the generated operation map:

same_retry = client.request_operation(
    "retryWebhookDelivery",
    path={"id": endpoints["data"][0]["id"], "delivery_id": "delivery-id"},
)

csv_again = client.request_operation(
    "exportAnalyticsVideos",
    query={"account": ["saved-account-id"]},
)

The SDK sends X-TokPortal-Client: tokportal-python/0.1.0 on API requests for observability and support diagnostics.

Verify signed webhook deliveries with the exact raw request body:

from tokportal import verify_webhook_signature

valid = verify_webhook_signature(
    raw_body,
    request.headers["TokPortal-Signature"],
    os.environ["TOKPORTAL_WEBHOOK_SECRET"],
)
from tokportal import TokPortalApiError

try:
    client.bundles.create({
        "bundle_type": "account_and_videos",
        "country": "USA",
        "videos_quantity": 5,
    })
except TokPortalApiError as error:
    print(error.status_code, error.code, error.details, error.request_id)
    if error.retryable:
        wait_seconds = error.retry_after_seconds or 1
        # Retry with backoff.
        pass
    print(error.rate_limit)

API keys use the format sk_ followed by 64 lowercase hex characters. TokPortal stores only a SHA-256 hash of the key and shows the raw key once at creation.

Source of truth

This package is generated from the TokPortal public OpenAPI schema (https://developers.tokportal.com/openapi.json) in the private TokPortal monorepo. Generated files (tokportal/_generated.py) are overwritten on every release — do not edit them by hand. See CONTRIBUTING.md for what we accept as PRs and SECURITY.md for vulnerability reporting.

MIT © TokPortal

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tokportal/tokportal-python'

If you have feedback or need assistance with the MCP directory API, please join our Discord server