# DealMachine API — Full Reference > Property intelligence platform for real estate investors. Search 160M+ US properties, enrich records by address/coordinates/APN/email/phone/name, manage lists, run comps, send mail campaigns, and manage a CRM pipeline. --- ## Authentication Most endpoints require an API key passed via the `Authorization` header: ``` Authorization: Bearer dm_sk_live_xxx ``` Keys use the prefix `dm_sk_live_` (production) or `dm_sk_test_` (sandbox). The `/v1/signup` and `/v1/plans` endpoints are public and require no authentication. OAuth access tokens (`dm_at_live_xxx`) are also accepted as Bearer tokens. ### Request Headers | Header | Description | |--------|-------------| | `Authorization` | Required. API key or OAuth access token in Bearer format | | `X-Request-Id` | Optional. Your request ID for distributed tracing | ### Response Headers | Header | Description | |--------|-------------| | `X-Request-Id` | Unique request ID for debugging and support | ## Base URL ``` https://api.v2.dealmachine.com ``` All endpoints are prefixed with `/v1/`. ## Rate Limits All API requests are rate limited per organization: - **60 requests per minute** - **5,000 requests per day** Rate limit status is returned in every response via headers: | Header | Description | |--------|-------------| | `X-RateLimit-Limit` | Requests allowed per minute | | `X-RateLimit-Remaining` | Requests remaining this minute | | `X-RateLimit-Reset` | Unix timestamp when the limit resets | | `X-RateLimit-Day-Limit` | Requests allowed per day | | `X-RateLimit-Day-Remaining` | Requests remaining today | When rate limited, you receive a `429` response with a `Retry-After` header. ## Credits Enrichment and search operations consume credits by entity type: - Properties: 1 property lead credit per unique property - Contacts/people: 1 people lead credit per unique person/contact, regardless of how many phones or emails are returned - Requests or exports that include both entity families charge both, based on selected fields and returned contacts - Future company search should follow people-family billing: 1 people lead credit per returned person - Re-accessing the same entity within a billing cycle is free (deduplication) - Count endpoints are free (no credits consumed) - Filter and field discovery endpoints are free - Use `GET /v1/usage` to check remaining credits - Use `estimate_cost: true` on search endpoints to preview credit cost without consuming credits ## Error Format All errors return a consistent JSON structure: ```json { "error": { "code": "error_code", "message": "Human readable message", "request_id": "req_xxx", "details": {} } } ``` ## Pagination Paginated endpoints return: ```json { "data": [...], "pagination": { "page": 1, "per_page": 25, "total_results": 1234, "total_pages": 50, "has_next_page": true, "has_previous_page": false } } ``` ## Subscription Lifecycle | Step | Endpoint | What happens | |------|----------|-------------| | Sign up | `POST /v1/signup` | Account created, API key returned. Status: `none`. | | Browse plans | `GET /v1/plans` | Returns all plans with pricing, features, and Stripe price IDs. | | Choose plan | `POST /v1/checkout` | Creates a Stripe checkout session. After checkout is completed, the subscription is active with full plan credits. | | Check status | `GET /v1/subscription` | Returns current subscription status, billing cycle, and credit caps. | | Check usage | `GET /v1/usage` | Returns credit usage for the current billing cycle with breakdown by entity type. | --- ## Common Schemas ### LocationInput Defines where to search. At least one location is required for search endpoints. Multiple locations use OR logic. ```json { "type": "state|county|zip_code|radius|polygon|bounds", "code": "TX", // for state (2-letter), county (5-digit FIPS), zip_code (5-digit) "latitude": 30.25, // for radius "longitude": -97.75, // for radius "radius_miles": 5, // for radius "coordinates": [[lng,lat], ...], // for polygon (min 3 points) "north": 30.5, "south": 30.0, "east": -97.5, "west": -98.0 // for bounds } ``` ### FilterInput Filters narrow search results. Use `GET /v1/filters` to discover available filters, their types, operators, and valid values. ```json { "filter_id": "estimated_value", "operator": "range", "value": { "min": 200000, "max": 500000 } } ``` ### SortInput ```json { "field_id": "estimated_value", "direction": "asc|desc" } ``` --- ## API Endpoints ### Account #### POST /v1/signup Create a new account and receive an API key. No authentication required. - **Rate limits:** 5 requests/hour, 20 requests/day per IP - **Request body:** - `email` (string, required) — Email address - `first_name` (string, optional) — First name - `last_name` (string, optional) — Last name - **Response (201):** `{ api_key, key_id, organization: { id, name, slug }, message }` #### GET /v1/plans List available subscription plans. No authentication required. - **Query params:** - `type` (string, optional) — `solo` or `scale` - **Response (200):** `{ plans: [{ id, name, description, type, pricing: { currency, monthly_price_cents, annual_price_cents, is_per_user }, credits: { enrichment_per_month, enrichment_per_year, ai_per_month }, features: [...], stripe_price_ids: { monthly, annual }, seats: { min, max } }], free_tier: { enrichment_credit_cap, ai_credit_cap } }` #### POST /v1/checkout Create a Stripe checkout session for subscribing to a plan. - **Request body:** - `price_id` (string, required) — Stripe price ID from `GET /v1/plans` - `quantity` (integer, optional, default 1) — Number of seats for seat-based plans - **Response (200):** `{ checkout_url, session_id, expires_at }` #### GET /v1/subscription Get current subscription status. - **Response (200):** `{ status, plan: { name, is_paid }, trial: { is_trialing, trial_end, enrichment_credits }, billing_cycle: { start, end }, credits: { enrichment_cap, ai_cap } }` - **Status values:** `active`, `trialing`, `past_due`, `canceled`, `incomplete`, `incomplete_expired`, `unpaid`, `paused`, `none` #### GET /v1/account Get account information. - **Response (200):** `{ organization: { id, name, createdAt }, auth: { type } }` #### GET /v1/usage Get credit usage for the current billing cycle. - **Response (200):** `{ plan: { name, is_paid }, billing_cycle: { start, end }, credits: { included, used, remaining, overage, breakdown: { properties, people, companies } } }` --- ### Properties #### POST /v1/properties/search Search properties with filters and locations. Costs 1 property lead credit per unique property searched, even when `anchor` returns people. - **Request body:** - `locations` (LocationInput[], required, 1-15) — Where to search (OR logic) - `filters` (FilterInput[], optional, max 50) — Filters to apply - `fields` (string[], optional) — Field IDs to include in results - `anchor` (string, optional) — `properties` (default) or `people` - `contact_audience` (string, optional) — `owners` (default), `owners_and_family`, `renters`, `residents`, `none` - `page` (integer, optional, default 1) - `per_page` (integer, optional, default 25, max 250) - `sort` (SortInput[], optional) - `estimate_cost` (boolean, optional) — Preview credit cost without consuming credits - **Response (200):** `{ totals: { properties, people }, data: [...], pagination: { page, per_page, total_results, total_pages, has_next_page, has_previous_page } }` #### POST /v1/properties/search/count Count properties matching filters. Free (no credits consumed). - **Request body:** - `locations` (LocationInput[], required, 1-15) - `filters` (FilterInput[], optional, max 50) - `anchor` (string, optional) — `properties` or `people` - `contact_audience` (string, optional) - **Response (200):** `{ total_properties, total_people, total_results }` #### POST /v1/properties/export Export properties as CSV (up to 1,000,000 records). Returns signed download URLs. - **Request body:** - `locations` (LocationInput[], optional) — Required unless `property_ids` is provided - `property_ids` (string[], optional, max 1000) — DealMachine property IDs - `filters` (FilterInput[], optional, max 50) - `fields` (string[], optional) - `contact_audience` (string, optional) - `anchor` (string, optional) — `property` (default), `person`, `phone`, `email` - `require_phone` (boolean, optional) - `require_email` (boolean, optional) - `mobile_only` (boolean, optional) - `landline_only` (boolean, optional) - `scrub_dnc` (boolean, optional) — Exclude Do Not Call contacts - **Response (200):** `{ export_id, status, record_count, file_count, total_file_size, execution_time, download_urls: [{ filename, url, size }], credits: { used, properties, people }, usage: { credits_used, credits_remaining, enrichment_credit_cap, billing_cycle_end } }` #### GET /v1/properties/{id} Get a single property by DealMachine ID. - **Path params:** `id` (string, required) - **Query params:** - `contact_audience` (string, optional) — `owners`, `owners_and_family`, `renters`, `residents`, `all` - `fields` (string, optional) — Comma-separated field IDs - **Response (200):** Property object with requested fields #### POST /v1/properties/ids Get multiple properties by IDs in a single batch. - **Request body:** - `ids` (string[], required, max 250) — DealMachine property IDs - `contact_audience` (string, optional) - `fields` (string[], optional) - **Response (200):** `{ data: [...], credits: { used, properties, people } }` --- ### People #### POST /v1/people/search Search people with filters and locations. Same structure as property search. - **Request body:** Same as `POST /v1/properties/search` - **Response (200):** `{ totals, data, pagination }` #### POST /v1/people/search/count Count people matching filters. Free. - **Request body:** Same as `POST /v1/properties/search/count` - **Response (200):** `{ total_properties, total_people, total_results }` #### POST /v1/people/export Export people as CSV. Same options as property export. - **Request body:** Same as `POST /v1/properties/export` - **Response (200):** Same as property export response #### GET /v1/people/{id} Get a single person by DealMachine ID. - **Path params:** `id` (string, required) - **Query params:** - `include_properties` (boolean, optional) - `fields` (string, optional) — Comma-separated field IDs - **Response (200):** Person object with requested fields #### POST /v1/people/ids Get multiple people by IDs in a single batch. - **Request body:** - `ids` (string[], required, max 250) - `include_properties` (boolean, optional) - `fields` (string[], optional) - **Response (200):** `{ data: [...], credits: { used, properties, people } }` --- ### Enrichment All enrichment endpoints accept batches via `data` array. Property enrichment costs 1 property lead credit per matched property, and contacts added with `contact_audience` consume people credits. People enrichment costs 1 people lead credit per matched person; `include_properties` consumes property credits for associated properties. Misses are free. #### POST /v1/enrichment/address Look up properties by street address. - **Request body:** - `data` (array, required) — Array of address objects. Each can have `full_address` (string) OR parsed components: `street`, `city`, `state`, `zip` - `contact_audience` (string, optional) — `owners`, `owners_and_family`, `renters`, `residents` - **Response (200):** Array of matched property records #### POST /v1/enrichment/reverse-geocode Look up properties by GPS coordinates. Returns closest property within 300 feet. - **Request body:** - `data` (array, required) — Array of `{ latitude, longitude }` objects - `contact_audience` (string, optional) - **Response (200):** Array of matched property records #### POST /v1/enrichment/latlng Deprecated. Use `/v1/enrichment/reverse-geocode` instead. Same interface. #### POST /v1/enrichment/apn Look up properties by Assessor's Parcel Number. - **Request body:** - `data` (array, required) — Array of `{ apn }` objects - `location` (object, optional) — `{ type: "state"|"zip_code"|"county", code: "XX" }` to narrow results - `contact_audience` (string, optional) - **Response (200):** Array of matched property records #### POST /v1/enrichment/email Look up people by email address. - **Request body:** - `data` (array, required) — Array of `{ email }` objects - `include_properties` (boolean, optional) — Include associated properties; returned properties consume property credits - **Response (200):** Array of matched person records #### POST /v1/enrichment/phone Look up people by phone number. - **Request body:** - `data` (array, required) — Array of `{ phone }` objects (e.g., `"5551234567"` or `"+15551234567"`) - `include_properties` (boolean, optional) - **Response (200):** Array of matched person records #### POST /v1/enrichment/name Look up people by name. Always narrow with a location to avoid broad, expensive results. - **Request body:** - `data` (array, required) — Array of `{ last_name, first_name?, middle_initial? }` objects - `location` (object, optional but strongly recommended) — `{ type: "state"|"zip_code"|"county", code: "XX" }` - `include_properties` (boolean, optional) - `page` (integer, optional, default 1) - `per_page` (integer, optional, default 25, max 100) - **Response (200):** Paginated array of matched person records --- ### Comparable Properties #### POST /v1/comps Find comparable properties (sales comps) for one or more properties. - **Request body:** - `property_ids` (string[], required) — DealMachine property IDs - `location` (object, optional) — `{ type: "radius", radius_miles: 1.5 }` - `criteria` (object, optional) — `{ timeframe: "3months"|"6months"|"12months"|"all", sort_by: "distance"|"price"|"date"|"match", limit: 25, include_foreclosures: false }` - **Response (200):** Comparable properties with value estimation --- ### Phones #### POST /v1/phones/dnc Check Do Not Call (DNC) registry status for phone numbers. - **Request body:** Array of phone numbers or phone objects - **Response (200):** DNC status for each number --- ### Addresses #### POST /v1/addresses/validate Validate and standardize addresses via USPS. - **Request body:** - `data` (array, required) — Array of address objects with `full_address` or parsed components - **Response (200):** Validation results (valid, corrected with corrections listed, or invalid with reason) --- ### Filters & Fields #### GET /v1/filters List all available search filters. Free. - **Query params:** - `source_type` (string, optional) — `properties` or `people` - `group_id` (string, optional) - `search` (string, optional) - `page` (integer, optional) - `per_page` (integer, optional) - **Response (200):** `{ data: [{ filter_id, name, type, operators: [...], values: [...], group_id }], pagination }` #### GET /v1/fields List all available data fields. Free. - **Query params:** - `source_type` (string, optional) — `properties` or `people` - `group_id` (string, optional) - `search` (string, optional) - `page` (integer, optional) - `per_page` (integer, optional) - **Response (200):** `{ data: [{ field_id, name, type, description, group_id }], pagination }` --- ### Locations #### GET /v1/locations List searchable locations (states, counties, ZIP codes). - **Query params:** - `type` (string, optional) — `state`, `county`, `zip_code` - `parent_code` (string, optional) — Parent location code - `search` (string, optional) - `page` (integer, optional) - `per_page` (integer, optional) - **Response (200):** `{ data: [...], pagination }` #### GET /v1/locations/{locationId} Get a specific location by ID. - **Path params:** `locationId` (string, required) - **Response (200):** Location object with metadata --- ### Lists #### POST /v1/lists Create a new list. Pass filters/locations to build asynchronously, or pass record_ids (max 250) to create pre-populated, or neither for an empty list. - **Request body:** - `name` (string, required) - `source_type` (string, optional) — `properties` or `people` - `record_ids` (string[], optional, max 250) - `locations` (LocationInput[], optional) — For async build - `filters` (FilterInput[], optional) — For async build - **Response (201):** Created list object #### GET /v1/lists List all lists with pagination and filtering. - **Query params:** - `search` (string, optional) - `source_type` (string, optional) - `sort` (string, optional) — `newest`, `oldest`, `name`, `count` - `page` (integer, optional) - `per_page` (integer, optional) - **Response (200):** `{ data: [...], pagination }` #### GET /v1/lists/{list_id} Get list details including status and progress. #### PUT /v1/lists/{list_id} Update (rename) a list. - **Request body:** `{ name }` #### DELETE /v1/lists/{list_id} Delete a list and all its items. #### POST /v1/lists/{list_id}/build Build a list from search filters (asynchronous). Poll `GET /v1/lists/{list_id}` for status. - **Request body:** - `locations` (LocationInput[], required) - `filters` (FilterInput[], optional) #### POST /v1/lists/{list_id}/import Import record IDs into an existing list. - **Request body:** - `ids` (string[], required) - `source_type` (string, optional) - `id_type` (string, optional) — `internal_property_id` or `internal_person_id` #### GET /v1/lists/{list_id}/items List items in a list with pagination. - **Query params:** `page`, `per_page` #### POST /v1/lists/{list_id}/items Add items to a list by ID. - **Request body:** `{ ids, id_type? }` #### DELETE /v1/lists/{list_id}/items Remove items from a list by ID. - **Request body:** `{ ids }` #### POST /v1/lists/{list_id}/export Export list items as CSV. - **Request body:** - `fields` (string[], optional) - `anchor` (string, optional) — `property` or `person` --- ### Activity #### POST /v1/activity/search Search past API activity with type filters and free-text search. - **Request body:** - `types` (string[], optional) — Activity type filter - `query` (string, optional) — Free-text search - `page` (integer, optional) - `per_page` (integer, optional) - **Response (200):** `{ data: [...], pagination }` #### GET /v1/activity/{activity_id} Get full details of a specific activity record. - **Response (200):** Activity object with original request, result summary, and entity IDs --- ### Exports #### GET /v1/exports List past exports with pagination and status filtering. - **Query params:** `limit` (1-100, default 25), `offset` (default 0), `status` (pending/processing/completed/failed) #### GET /v1/exports/{exportId} Get export details by ID. --- ### Tasks #### GET /v1/tasks List tasks with optional status and priority filters. - **Query params:** `status`, `priority`, `page`, `per_page` #### POST /v1/tasks Create a new task. #### GET /v1/tasks/{id} Get task details. #### PUT /v1/tasks/{id} Update a task. #### DELETE /v1/tasks/{id} Delete a task. --- ### CRM CRM endpoints require the CRM app to be enabled. If not enabled, all `/v1/crm/*` endpoints return `403 crm_not_enabled`. #### GET /v1/crm/pipelines List all pipelines. #### GET /v1/crm/opportunities List opportunities with filters and pagination. - **Query params:** `pipeline_id`, `stage_id`, `label_id`, `search`, `sort`, `page`, `per_page` #### POST /v1/crm/opportunities Create a new opportunity. #### GET /v1/crm/opportunities/{id} Get opportunity details. #### PUT /v1/crm/opportunities/{id} Update an opportunity. #### DELETE /v1/crm/opportunities/{id} Delete an opportunity. #### POST /v1/crm/opportunities/{id}/move Move an opportunity to a different stage. #### GET /v1/crm/labels List all labels. #### POST /v1/crm/labels Create a label. #### PUT /v1/crm/labels/{id} Update a label. #### DELETE /v1/crm/labels/{id} Delete a label. #### GET /v1/crm/opportunities/{id}/tasks List tasks for an opportunity. #### POST /v1/crm/opportunities/{id}/tasks Create a task on an opportunity. #### PUT /v1/crm/opportunities/{id}/tasks/{subId} Update a task on an opportunity. #### GET /v1/crm/opportunities/{id}/notes List notes for an opportunity. #### POST /v1/crm/opportunities/{id}/notes Create a note on an opportunity. #### PUT /v1/crm/opportunities/{id}/notes/{subId} Update a note on an opportunity. #### GET /v1/crm/opportunities/{id}/records List linked records for an opportunity. #### POST /v1/crm/opportunities/{id}/records Link a property or person record to an opportunity. #### DELETE /v1/crm/opportunities/{id}/records/{subId} Unlink a record from an opportunity. #### POST /v1/crm/opportunities/{id}/labels Add a label to an opportunity. #### DELETE /v1/crm/opportunities/{id}/labels/{subId} Remove a label from an opportunity. #### GET /v1/crm/opportunities/{id}/activity Get the activity feed for an opportunity. #### GET /v1/crm/opportunities/{id}/subscribers List subscribers to an opportunity. #### POST /v1/crm/opportunities/{id}/subscribers Subscribe to an opportunity. #### DELETE /v1/crm/opportunities/{id}/subscribers Unsubscribe from an opportunity. --- ### Mail Mail endpoints require Mail app API access to be enabled. If not enabled, all `/v1/mail/*` endpoints return `403 mail_not_enabled`. Designs are HTML-based. Generation from prompts is instant. Sending mail requires sufficient wallet credits. #### Campaigns - `GET /v1/mail/campaigns` — List campaigns (with pagination, status filter) - `POST /v1/mail/campaigns` — Create a campaign - `GET /v1/mail/campaigns/{id}` — Get campaign details - `PUT /v1/mail/campaigns/{id}` — Update a campaign - `POST /v1/mail/campaigns/{id}/send` — Launch a campaign - `POST /v1/mail/campaigns/{id}/pause` — Pause a campaign - `POST /v1/mail/campaigns/{id}/resume` — Resume a campaign - `DELETE /v1/mail/campaigns/{id}` — Cancel a campaign - `GET /v1/mail/campaigns/{id}/recipients` — List campaign recipients - `GET /v1/mail/campaigns/{id}/analytics` — Campaign delivery analytics - `GET /v1/mail/campaigns/{id}/cost-estimate` — Estimate campaign cost #### Designs - `GET /v1/mail/designs` — List designs - `POST /v1/mail/designs` — Create a design (HTML-based) - `GET /v1/mail/designs/{id}` — Get design details - `PUT /v1/mail/designs/{id}` — Update a design - `DELETE /v1/mail/designs/{id}` — Delete a design #### Wallet - `GET /v1/mail/wallet/balance` — Get wallet balance - `POST /v1/mail/wallet/add-funds` — Add wallet credits - `GET /v1/mail/wallet/transactions` — List wallet transactions - `GET /v1/mail/wallet/pricing` — Get postcard pricing (cents per postcard) #### Return Addresses - `GET /v1/mail/return-addresses` — List return addresses - `POST /v1/mail/return-addresses` — Create return address (USPS validated) - `PUT /v1/mail/return-addresses/{id}` — Update return address - `DELETE /v1/mail/return-addresses/{id}` — Delete return address - `POST /v1/mail/return-addresses/{id}/default` — Set default return address --- ### OAuth #### GET /v1/oauth/authorize Start OAuth 2.0 authorization code flow. Redirects to consent page. - **Query params:** - `response_type` (string, required) — Must be `code` - `client_id` (string, required) — Your application client ID - `redirect_uri` (string, required) — Must match registered redirect URI - `scope` (string, required) — Space-separated scopes (e.g., `account:read`) - `state` (string, optional) — CSRF protection - `code_challenge` (string, optional) — PKCE code challenge - `code_challenge_method` (string, optional) — `S256` (recommended) or `plain` #### POST /v1/oauth/token Exchange authorization code or refresh token for access tokens. - **Request body (authorization_code):** - `grant_type`: `authorization_code` - `client_id`, `client_secret`, `code`, `redirect_uri`, `code_verifier` (if PKCE) - **Request body (refresh_token):** - `grant_type`: `refresh_token` - `client_id`, `client_secret`, `refresh_token` - **Response (200):** `{ access_token, token_type: "Bearer", expires_in: 3600, refresh_token, scope }` - **Token lifetimes:** Access tokens: 1 hour. Refresh tokens: 30 days. Refresh tokens are rotated on each use. #### POST /v1/oauth/revoke Revoke an access or refresh token (RFC 7009). Always returns 200. - **Request body:** `{ token, token_type_hint? }` #### GET /v1/oauth/userinfo Get information about the current access token. --- ### Device Auth (RFC 8628) For CLI and headless applications. #### POST /v1/auth/device/code Request a device authorization code. No authentication required. - **Request body:** - `client_id` (string, required) — `dealmachine-next-cli` or `dealmachine-next-mcp` - `device_name` (string, optional) - **Response (200):** `{ device_code, user_code, verification_uri, verification_uri_complete, expires_in, interval }` #### POST /v1/auth/device/token Poll for device authorization token. No authentication required. - **Request body:** - `device_code` (string, required) - `client_id` (string, required) - **Response (200):** `{ api_key, key_id, organization: { id, name, slug } }` - **Polling responses:** `authorization_pending` (keep polling), `slow_down` (increase interval by 5s), `access_denied`, `expired_token` --- ## MCP Server The DealMachine MCP server provides property intelligence MCP tools for any AI agent via the Model Context Protocol. Works with Claude, ChatGPT, Cursor, VS Code Copilot, Windsurf, and every MCP-compatible client. ### Installation ```bash npm install -g @dealmachine/mcp ``` ### Available MCP Tools (12 total) #### Enrichment MCP Tools | MCP Tool | Description | Cost | |----------|-------------|------| | `dealmachine_enrich_address` | Property lookup by street address (full or parsed components). Returns owner info, valuation, mortgage details, and more. | 1 credit/match | | `dealmachine_enrich_latlng` | Property lookup by GPS coordinates. Returns closest property within 300 ft. | 1 credit/match | | `dealmachine_enrich_apn` | Property lookup by Assessor's Parcel Number. API normalizes formatting automatically. | 1 credit/match | | `dealmachine_enrich_email` | Person lookup by email address. Returns contact info, demographics, and optionally associated properties. | 1 credit/match | | `dealmachine_enrich_phone` | Person lookup by phone number. Returns contact info, demographics, and optionally associated properties. | 1 credit/match | | `dealmachine_enrich_name` | Person lookup by name. Always narrow with a location to avoid broad, expensive results. | 1 credit/person | #### Discovery MCP Tools | MCP Tool | Description | Cost | |----------|-------------|------| | `dealmachine_filters` | List all available search filters with types, operators, and options. Always call before constructing searches. | Free | | `dealmachine_fields` | List all available data fields for search results. Call before passing a fields array to search. | Free | #### Account MCP Tools | MCP Tool | Description | Cost | |----------|-------------|------| | `dealmachine_usage` | Credit usage for the current billing cycle. Check before large operations. | Free | #### Authentication MCP Tools | MCP Tool | Description | Cost | |----------|-------------|------| | `dealmachine_login` | Authenticate via API key or browser-based device auth. Must authenticate before using other MCP tools. | Free | | `dealmachine_logout` | Remove stored credentials. Use to switch accounts. | Free | | `dealmachine_whoami` | Check auth status and active organization. Use `verify: true` to test credentials. | Free | --- ## CLI The DealMachine CLI (`dm`) provides property intelligence from the command line. It is a standalone binary with zero internal dependencies — it communicates exclusively through the public API. ### Installation ```bash npm install -g @dealmachine/cli dm login ``` ### Authentication ```bash dm login # Browser-based device auth (recommended) dm login --key dm_sk_live_xxx # Direct API key (for CI/scripts) dm logout # Remove credentials dm whoami --verify # Check auth status ``` ### Command Reference #### Properties - `dm properties search --body '{...}'` — Search properties with filters and locations - `dm properties search -f search.json` — Search from a JSON file - `dm properties count --body '{...}'` — Count matching properties (free) - `dm properties get ` — Get a single property - `dm properties ids ...` — Get multiple properties by ID - `dm properties export -f search.json` — Export as CSV #### People - `dm people search --body '{...}'` — Search people - `dm people count -f filters.json` — Count matching people (free) - `dm people get ` — Get a single person - `dm people ids ` — Get multiple people by ID - `dm people export -f search.json` — Export as CSV #### Enrichment - `dm enrich address "123 Main St, Austin, TX 78704"` — By address - `dm enrich latlng 30.25,-97.75` — By coordinates - `dm enrich apn "0123-456-789" --state TX` — By parcel number - `dm enrich email jane@example.com` — By email - `dm enrich phone 5125551234` — By phone - `dm enrich name "Jane Doe" --state TX` — By name All enrich commands support batch input: `--body` for JSON, `-f` for JSON or CSV files, or piped stdin. CSV batches over 250 items are automatically chunked. #### Comps - `dm comps ` — Find comparable properties - `dm comps --radius 2 --timeframe 12months --limit 50` #### Lists - `dm lists search` — List all lists - `dm lists create --name "Austin Leads"` — Create a list - `dm lists get ` — Get list details - `dm lists update --name "New Name"` — Rename - `dm lists delete ` — Delete - `dm lists build -f search.json` — Build from filters (async) - `dm lists import --ids 111,222 --source-type properties` — Import IDs - `dm lists items ` — List items - `dm lists add --ids 111,222` — Add items - `dm lists remove --ids 111,222` — Remove items - `dm lists export ` — Export #### Discovery & Account - `dm filters` — List available filters - `dm fields` — List available fields - `dm account` — Show account info - `dm usage` — Show credit usage - `dm activity search` — Search API activity - `dm activity get ` — Get activity details - `dm addresses validate "123 Main St, Austin, TX"` — Validate addresses #### Configuration - `dm config get` — Show all config - `dm config set apiEnvironment production` — Set environment - `dm config path` — Print config file path #### Global Options All commands support `--json` for machine-readable output, `--help`, and `--version`. #### Input Methods Commands accepting request bodies support three input methods: 1. `--body ` — Inline JSON string 2. `-f, --file ` — Read from JSON or CSV file 3. Piped stdin — `cat query.json | dm properties search` --- ## Links - Full documentation: - OpenAPI spec: - Concise LLM overview: - CLI package: `npm install -g @dealmachine/cli` - MCP Server package: `npm install -g @dealmachine/mcp` - GitHub: - Support: support@dealmachine.com