# Motionworks AI - Extended API Reference > Complete endpoint reference for AI agents and LLM tool integrations. > Version: 2.0.0 | Updated: 2026-04-19 ## Authentication All authenticated endpoints require an `X-API-Key` header. Obtain a sandbox key via POST /signup (no auth required). ## Base URL https://api2.mworks.com/v2 ## Rate Limits | Tier | Requests/min | Credits/month | Annual Cost | |--------------|-------------|---------------|----------------| | Sandbox | 10 | 1,000 | Free | | Professional | 100 | 50,000 | $12,000/yr | | Enterprise | 500 | 200,000 | $72,000/yr | | Strategic | 1,000 | Unlimited | Custom | Rate limit headers returned on every response: - X-RateLimit-Limit: requests allowed per minute - X-RateLimit-Remaining: requests remaining in window - X-RateLimit-Reset: epoch seconds until window resets - X-Credits-Remaining: credits remaining in billing period --- ## Endpoints ### Profiles #### GET /profiles/{id} Credit cost: 1 Returns a single Viewcast profile with all measurement fields. Path parameters: - id (string, required): Profile ID (Motionworks place_id or spot_id) Query parameters: - fields (string, optional): Comma-separated list of fields to return - include (string, optional): Additional data to include: "geopath", "demographics", "history" Response fields: - place_id (string): Unique identifier for the physical location - spot_id (string): Unique identifier for the advertising spot - location.lat (number): Latitude - location.lng (number): Longitude - market.id (string): DMA market identifier - market.name (string): DMA market name - construction_type (string): e.g., "Bulletin", "Poster", "Street Furniture" - classification_type (string): TAB classification - media_type (string): e.g., "Roadside", "Transit", "Place-Based" - digital (boolean): Whether the unit is digital - face_orientation (string): Compass direction the face is oriented - face_height (number): Height in feet - face_width (number): Width in feet - viewable_distance (number): Maximum viewable distance in feet - face_audit_status (string): Audit status - period_start (string, ISO 8601): Measurement period start date - period_end (string, ISO 8601): Measurement period end date - period_days (integer): Number of days in measurement period - traffic (integer): Average daily traffic count - face_circ (integer): Face-level circulation (persons passing) - face_ots (integer): Face-level opportunity-to-see - face_lts (integer): Face-level likelihood-to-see - spot_circ (integer): Spot-level circulation - spot_ots (integer): Spot-level opportunity-to-see - spot_lts (integer): Spot-level likelihood-to-see - dwell (number): Average dwell time in seconds - percent_circ (object): Demographic and daypart percentage breakdowns - reach (object): Reach and frequency metrics - geopath (object): Geopath-supplied data fields (when available) - assignment_status (string): Current assignment status #### GET /profiles Credit cost: 5 List and search profiles with filters. Returns paginated results. Query parameters: - market_id (string, optional): Filter by DMA market ID - media_type (string, optional): Filter by media type - construction_type (string, optional): Filter by construction type - digital (boolean, optional): Filter digital/static - operator (string, optional): Filter by operator name - min_ots (integer, optional): Minimum OTS threshold - max_ots (integer, optional): Maximum OTS threshold - bbox (string, optional): Bounding box as "sw_lat,sw_lng,ne_lat,ne_lng" - updated_since (string, optional): ISO 8601 date; return only profiles updated after this date - cursor (string, optional): Pagination cursor from previous response - limit (integer, optional): Results per page, 1-100, default 25 Response: - data (array): Array of profile objects - pagination.cursor (string): Cursor for next page (null if last page) - pagination.total (integer): Total matching profiles - pagination.has_more (boolean): Whether more pages exist #### GET /profiles/{id}/history Credit cost: 10 Historical measurement data for a profile. Path parameters: - id (string, required): Profile ID Query parameters: - granularity (string, optional): "by_year", "by_month", or "by_week" (default: "by_month") - start_date (string, optional): ISO 8601 start date (default: earliest available) - end_date (string, optional): ISO 8601 end date (default: current period) Response: - profile_id (string): Profile identifier - granularity (string): Requested granularity - periods (array): Array of measurement period objects with all metric fields #### GET /profiles/{id}/viewshed Credit cost: 15 Visibility analysis from the profile's location. Path parameters: - id (string, required): Profile ID Query parameters: - radius (integer, optional): Analysis radius in feet (default: 1000, max: 5280) - include_obstacles (boolean, optional): Include obstacle analysis (default: false) Response: - profile_id (string): Profile identifier - viewshed_polygon (GeoJSON): Visible area polygon - visibility_score (number): 0-1 visibility score - approach_vectors (array): Primary approach directions with visibility metrics - obstacles (array): Identified obstacles (when include_obstacles=true) #### GET /profiles/{id}/audience Credit cost: 20 Demographic composition and daypart breakdowns. Path parameters: - id (string, required): Profile ID Query parameters: - segments (string, optional): Comma-separated segment IDs to include - dayparts (boolean, optional): Include daypart breakdowns (default: true) Response: - profile_id (string): Profile identifier - demographics (object): Age, gender, income, ethnicity breakdowns - dayparts (object): Audience by time-of-day and day-of-week - segments (array): Matched audience segment data - index (object): Index values vs. market average #### POST /profiles/bulk Credit cost: 50 Batch retrieval of multiple profiles. Request body (JSON): - ids (array, optional): Array of profile IDs (max 500) - filters (object, optional): Same filter parameters as GET /profiles - fields (array, optional): Fields to include in response Response: - data (array): Array of profile objects - meta.total (integer): Total profiles returned - meta.credits_consumed (integer): Actual credits used ### Markets #### GET /markets Credit cost: 1 List all DMA markets with profile counts. Query parameters: - include_metrics (boolean, optional): Include aggregated market metrics (default: false) Response: - data (array): Array of market objects with id, name, profile_count #### GET /markets/{id} Credit cost: 1 Market detail with aggregated metrics. Path parameters: - id (string, required): DMA market ID Response: - id (string): Market identifier - name (string): Market name - profile_count (integer): Number of measured profiles - metrics (object): Aggregated OTS, circulation, and LTS totals - media_type_breakdown (object): Profile counts by media type ### Audience Segments #### GET /segments Credit cost: 1 List available audience segments. Response: - data (array): Array of segment objects with id, name, description, category #### GET /segments/{id} Credit cost: 1 Segment detail. Path parameters: - id (string, required): Segment ID Response: - id (string): Segment identifier - name (string): Segment name - description (string): Segment description - category (string): Segment category - universe_size (integer): Estimated population in segment ### Account Management #### POST /signup Credit cost: 0 (no auth required) Create a new sandbox account. Request body (JSON): - email (string, required): Account email - company (string, required): Company name - use_case (string, optional): Intended use case description Response: - account_id (string): New account ID - api_key (string): Sandbox API key - tier (string): "sandbox" - message (string): Verification email sent notice #### POST /signup/verify Credit cost: 0 Verify email address. Request body (JSON): - token (string, required): Verification token from email #### GET /keys Credit cost: 0 List API keys for the authenticated account. #### POST /keys Credit cost: 0 Create a new API key. Request body (JSON): - name (string, required): Key label - scopes (array, optional): Permission scopes (default: ["read"]) #### POST /keys/{id}/rotate Credit cost: 0 Rotate an API key. Returns new key; old key valid for 24 hours. #### DELETE /keys/{id} Credit cost: 0 Revoke an API key immediately. ### Billing #### GET /billing/usage Credit cost: 0 Credit usage for current billing period. Response: - period_start (string): Billing period start - period_end (string): Billing period end - credits_used (integer): Credits consumed - credits_limit (integer): Credit allowance - usage_by_endpoint (object): Breakdown by endpoint #### GET /billing/credits Credit cost: 0 Current credit balance. Response: - credits_remaining (integer): Credits left in period - credits_limit (integer): Total credit allowance - resets_at (string): Next reset timestamp #### POST /billing/upgrade Credit cost: 0 Request tier upgrade. Request body (JSON): - tier (string, required): "sandbox", "professional", "enterprise", or "strategic" - billing_email (string, optional): Billing contact ### Health #### GET /health Credit cost: 0 (no auth required) API health status. Response: - status (string): "healthy" or "degraded" - version (string): API version - timestamp (string): Current server time #### GET /health/data Credit cost: 0 (no auth required) Data freshness information. Response: - latest_period (string): Most recent measurement period - profiles_updated (integer): Profiles updated in latest refresh - next_refresh (string): Expected next data refresh date - coverage.markets (integer): Number of markets covered - coverage.profiles (integer): Total measured profiles --- ## Error Codes | Code | Meaning | Retry? | |------|----------------------------|--------| | 400 | Bad request / invalid params | No | | 401 | Missing or invalid API key | No | | 403 | Insufficient permissions | No | | 404 | Resource not found | No | | 409 | Conflict (duplicate signup) | No | | 422 | Validation error | No | | 429 | Rate limit exceeded | Yes (after X-RateLimit-Reset) | | 500 | Internal server error | Yes (with backoff) | | 503 | Service unavailable | Yes (with backoff) | Error response format: ```json { "error": { "code": "RATE_LIMITED", "message": "Rate limit exceeded. Retry after 32 seconds.", "retry_after": 32 } } ``` --- ## Agent Best Practices 1. Check credits before expensive calls: Call GET /billing/credits before batch or high-credit operations. Avoid burning credits on calls that will fail at the limit. 2. Use bulk endpoints: POST /profiles/bulk (50 credits) is far cheaper than multiple individual GET /profiles/{id} calls (1 credit each) when retrieving 50+ profiles. 3. Use cursor pagination: Never try to retrieve all results in one call. Follow the pagination.cursor to iterate through large result sets. 4. Filter server-side: Use query parameters (market_id, media_type, bbox, min_ots) rather than fetching all profiles and filtering client-side. 5. Cache market and segment lists: GET /markets and GET /segments change infrequently. Cache these responses for the duration of a session. 6. Use updated_since for incremental sync: When maintaining a local copy, use the updated_since parameter to fetch only changed profiles since last sync. 7. Check data freshness: Call GET /health/data to verify the latest measurement period before running analysis on time-sensitive queries. 8. Subscribe to webhooks for data refreshes: Contact enterprise support to configure webhook notifications for monthly data refresh events. 9. Handle rate limits gracefully: On 429 responses, wait the duration specified in retry_after before retrying. Do not retry immediately. 10. Use the fields parameter: When you only need specific fields, use the fields parameter on GET /profiles/{id} to reduce response size and improve latency. --- ## MCP Tool Summary The Motionworks MCP server exposes the following tools for agent integration: - get_profile: Retrieve a single OOH profile (1 credit) - search_profiles: Search and filter profiles (5 credits) - get_profile_history: Historical measurement data (10 credits) - get_profile_viewshed: Visibility analysis (15 credits) - get_profile_audience: Demographics and dayparts (20 credits) - bulk_profiles: Batch profile retrieval (50 credits) - list_markets: List DMA markets (1 credit) - get_market: Market detail (1 credit) - list_segments: List audience segments (1 credit) - get_segment: Segment detail (1 credit) - check_credits: Current credit balance (0 credits) - check_data_freshness: Data freshness status (0 credits) MCP server config: https://api2.mworks.com/.well-known/mcp.json --- ## Measurement Framework Motionworks uses a three-tier measurement methodology: 1. Circulation: Total persons passing within the viewable area of an OOH unit. 2. Opportunity-to-See (OTS): Subset of circulation with geometric line-of-sight to the face, adjusted for speed, dwell, and approach angle. 3. Likelihood-to-See (LTS): Modeled probability of actual visual engagement, incorporating attention factors, clutter, and environmental context. All measurements are reported at both face-level and spot-level granularity. --- ## Links - Summary: https://mworks.com/llms.txt - MCP config: https://api2.mworks.com/.well-known/mcp.json - OpenAPI spec: https://api2.mworks.com/openapi.json - Pricing: https://mworks.com/cx/pricing/calculator/ - Website: https://mworks.com