Skip to main content

Status polling pattern

The typical polling flow looks like this:
1

Create submission

POST to create a submission and read the submissionId.
2

Poll status

GET the submission at regular intervals.
3

Check completion

When the submission is complete or failed, stop polling.
4

Retrieve results

Fetch each asset’s result and its issues or clustered topics.

Getting submission status

Use GET /api/integrations/submissions/{submission_id} — there is no /status suffix.
curl -H "X-API-Key: your-api-key-here" \
  https://mm-midmarket-integrations-api-preview.azurewebsites.net/api/integrations/submissions/550e8400-e29b-41d4-a716-446655440000
Response:
{
  "submissionId": "550e8400-...",
  "workflowId": "asset-processing-...",
  "workspaceId": "660e8400-...",
  "status": "processing",
  "progressPercent": 40,
  "createdAt": "2026-06-03T12:00:00Z",
  "completedAt": null,
  "errorMessage": null,
  "assets": [
    {
      "assetId": "a1b2...",
      "versionGroupId": "g1...",
      "versionNumber": 1,
      "issueCount": null,
      "errorMessage": null,
      "errorType": null,
      "isRetryable": false,
      "retryCount": 0
    }
  ],
  "isRetryable": false
}
Per-asset failures are reported as flat fields — errorMessage, errorType, isRetryable, retryCount — not a nested error object. The terminal submission statuses are complete and failed.

Polling strategy

Basic polling loop

import time
import requests

API_BASE = "https://mm-midmarket-integrations-api-preview.azurewebsites.net"

def wait_for_completion(
    submission_id: str,
    api_key: str,
    max_wait_seconds: int = 300,
    poll_interval: int = 5,
) -> dict:
    """Poll until the submission reaches a terminal state."""

    start_time = time.time()

    while time.time() - start_time < max_wait_seconds:
        response = requests.get(
            f"{API_BASE}/api/integrations/submissions/{submission_id}",
            headers={"X-API-Key": api_key},
        )
        status = response.json()

        if status["status"] in ("complete", "failed"):
            return status

        print(f"Status: {status['status']}, waiting {poll_interval}s...")
        time.sleep(poll_interval)

    raise TimeoutError(
        f"Submission did not finish within {max_wait_seconds} seconds"
    )

# Usage
status = wait_for_completion(
    "550e8400-e29b-41d4-a716-446655440000", "your-api-key-here"
)
print(f"Final status: {status['status']}")

Exponential backoff

For long-running submissions, start with short intervals and back off over time:
import time
import requests

API_BASE = "https://mm-midmarket-integrations-api-preview.azurewebsites.net"

def wait_with_backoff(
    submission_id: str,
    api_key: str,
    initial_interval: float = 2,
    max_interval: float = 30,
    max_wait_seconds: int = 600,
) -> dict:
    """Poll with exponential backoff until terminal."""

    start_time = time.time()
    current_interval = initial_interval

    while time.time() - start_time < max_wait_seconds:
        response = requests.get(
            f"{API_BASE}/api/integrations/submissions/{submission_id}",
            headers={"X-API-Key": api_key},
        )
        status = response.json()

        if status["status"] in ("complete", "failed"):
            return status

        time.sleep(current_interval)
        current_interval = min(current_interval * 1.5, max_interval)

    raise TimeoutError(
        f"Submission did not finish within {max_wait_seconds} seconds"
    )

Retrieving results

Once an asset is done, fetch its result with GET /api/integrations/submissions/{submission_id}/assets/{asset_id}. The result includes the asset’s status, issueCount, and an issues array. For the flat enriched issue list call .../assets/{asset_id}/issues; for AI-clustered topics call .../assets/{asset_id}/topics.
curl -H "X-API-Key: your-api-key-here" \
  https://mm-midmarket-integrations-api-preview.azurewebsites.net/api/integrations/submissions/550e8400-.../assets/a1b2.../issues
Asset result shape:
{
  "assetId": "a1...",
  "submissionId": "s1...",
  "filename": "campaign-video.mp4",
  "mimeType": "video/mp4",
  "status": "complete",
  "issueCount": 2,
  "versionNumber": 1,
  "versionGroupId": "g1...",
  "issues": [
    {
      "id": "i1...",
      "referenceNumber": 1,
      "ruleCode": "CAP_8.4",
      "summary": "Unsubstantiated pricing claim",
      "severity": "MAJOR",
      "status": "OPEN"
    }
  ]
}
Issue severity is one of CRITICAL | MAJOR | MINOR | INFO, and issue status is one of OPEN | ACKNOWLEDGED | AWAITING_HUMAN_INPUT | RESOLVED | DISMISSED. The clustered topics endpoint returns { "assetId", "topics", "orphanIssues", "metadata" }. Topics group two or more related issues; orphanIssues are unclustered singletons and are normal. The issues and topics endpoints return 404 if the asset has not finished processing yet.

Complete polling example

A full end-to-end example:
import requests
import time

API_KEY = "your-api-key-here"
API_BASE = "https://mm-midmarket-integrations-api-preview.azurewebsites.net"

def submit_and_wait(file_paths: list[str]) -> list[dict]:
    """Upload files, submit, wait for completion, and return results."""

    # Step 1: Upload files (direct upload for simplicity) and collect assets
    assets = []
    for file_path in file_paths:
        with open(file_path, "rb") as f:
            upload = requests.post(
                f"{API_BASE}/api/integrations/uploads/direct",
                headers={"X-API-Key": API_KEY},
                files={"file": f},
            )
        upload.raise_for_status()
        assets.append({"blobPath": upload.json()["blobPath"]})

    # Create the submission
    submission = requests.post(
        f"{API_BASE}/api/integrations/submissions",
        headers={"X-API-Key": API_KEY},
        json={"assets": assets, "sidekickIds": []},
    ).json()
    submission_id = submission["submissionId"]
    print(f"Created submission: {submission_id}")

    # Step 2: Poll until terminal
    status = {}
    start_time = time.time()
    while time.time() - start_time < 600:
        status = requests.get(
            f"{API_BASE}/api/integrations/submissions/{submission_id}",
            headers={"X-API-Key": API_KEY},
        ).json()
        print(f"  status={status['status']} progress={status['progressPercent']}%")
        if status["status"] in ("complete", "failed"):
            break
        time.sleep(5)

    # Step 3: Retrieve each asset's result
    results = []
    for asset in status["assets"]:
        result = requests.get(
            f"{API_BASE}/api/integrations/submissions/{submission_id}"
            f"/assets/{asset['assetId']}",
            headers={"X-API-Key": API_KEY},
        ).json()
        if result["status"] == "complete":
            results.append(result)

    print(f"Completed processing {len(results)} assets")
    return results

# Usage
results = submit_and_wait(["video1.mp4", "video2.mp4"])
for result in results:
    print(f"\n{result['filename']}: {result['issueCount']} issues")
    for issue in result["issues"]:
        print(f"  [{issue['severity']}] {issue['summary']}")

Polling recommendations

  • Start with a poll interval of a few seconds for fast feedback.
  • Back off over time to avoid hammering the API.
  • Use progressPercent to surface progress to your users.
  • Set a reasonable timeout to prevent infinite waits.
  • Prefer webhooks over tight polling for production volume.

Polling vs webhooks

FactorPollingWebhooks
LatencyDelayed by poll intervalReal-time
ComplexitySimpler to implementRequires an endpoint
ThroughputBetter for few submissionsBetter for many
LoadHigher on the APILower on the API
For production systems with high volume, webhooks are recommended. See Webhook subscriptions.

Handling timeouts

If a submission does not finish within your timeout:
  1. Re-fetch the submission later — it may simply still be processing.
  2. Check the per-asset errorMessage, errorType, and isRetryable fields.
  3. Retry a failed, retryable asset with POST .../assets/{asset_id}/retry.
  4. Switch to webhooks for more reliable completion detection.