Shoply AI

How to Use the Shoply Merchant API

The Shoply Merchant API lets a merchant securely use their own Shoply data in reporting tools, support workflows, scheduled exports, and private business applications.

Each integration authenticates with a revocable, store-scoped credential created in Shoply Settings. You do not need to share a Shopify Admin API access token, and an integration can access only the store that issued its credential.

What Can I Do With the Merchant API?

You can use the Merchant API to:

  • Export daily, weekly, or monthly performance results to a spreadsheet or data warehouse.
  • Build a private dashboard for sessions, chats, searches, product clicks, add-to-cart events, purchases, and attributed revenue.
  • Monitor customer activity and discover unusual changes in engagement.
  • Review chat and search sessions for support, merchandising, and answer-quality work.
  • Retrieve an individual conversation timeline when investigating a customer issue.
  • Create, update, list, and delete the custom knowledge used by Shoply AI.
  • Add an external webpage or an uploaded PDF, text, or Markdown file to the knowledge base.
  • Align UTC event data with the store’s local timezone.

This guide documents seven endpoints. Five are the same endpoints used by Shoply Admin → Analytics, while two manage the knowledge base:

APICategoryWhat it returns
admin_store_analyticsPerformanceDaily, weekly, or monthly performance rows and totals
query_api_usage_countEngagementActivity counts grouped by UTC minute or day
query_sessionized_chatConversationsChat and search session summaries
query_log_visualizationConversationsThe complete event timeline for one selected session
admin_get_store_metadataStore configurationThe store timezone used to display UTC data correctly
admin_store_knowledgeKnowledgeCustom text, external pages, uploaded files, deletion, and recrawling
user_generated_fileKnowledge filesUploads a PDF, text, or Markdown file before it is registered as knowledge

The response bodies below are representative samples. Your fields and values will reflect the activity recorded for your store.

Deployment requirement: Store-owner secret authentication must be deployed on the API server you call. If a valid secret returns HTTP 403 with No access_token found, that server is still running the older access-token-only authentication code.

How Do I Create an API Secret?

  1. Open your Shoply AI admin.
  2. Go to Settings → Store owner API secret.
  3. Create a secret and give it a name that identifies the integration, such as Weekly reporting export.
  4. Copy the secret when it appears. Shoply shows the plaintext value only once.
Shoply Settings showing the Store owner API secret panel, Create API secret button, expiry date, and revoke control

Each secret is valid for 90 days. A store can have separate secrets for separate integrations, and you can revoke one secret without interrupting the others.

Save these values as server-side environment variables:

export SHOPLY_STORE_KEY="your-store.myshopify.com" export SHOPLY_STORE_OWNER_API_SECRETE="shoply_owner_key_example123.secret-value"

Use the store’s permanent myshopify.com domain for SHOPLY_STORE_KEY, even if shoppers normally visit a custom domain.

How Does Authentication Work?

Every Merchant API request sends an Authorization header whose value is a JSON string:

{ "store_key": "your-store.myshopify.com", "store_owner_api_secrete": "YOUR_STORE_OWNER_API_SECRET" }

store_owner_api_secrete is the exact public field name, including its historical spelling. This is not a Bearer token. Put the complete JSON string directly in the Authorization header.

Keep the secret in backend code only. Do not place it in a URL, browser bundle, public repository, analytics event, or application log.

Are There Official Python and Node.js Packages?

Yes. Shoply publishes two server-side packages that add the required authentication automatically and expose the Merchant API as typed functions. Both packages are open source under the shoplyai-public GitHub organization.

Python Package

The shoply-merchant-api package supports Python 3.12 and uses Pydantic v2 response models. Install the pinned 2.1.3 release:

python3.12 -m venv .venv source .venv/bin/activate python -m pip install "git+https://github.com/shoplyai-public/shoply-merchant-api.git@v2.1.3"

Retrieve the newest chat and search sessions as typed SessionSummary models:

import os from shoply_merchant_api import ShoplyMerchantClient with ShoplyMerchantClient( store_key=os.environ["SHOPLY_STORE_KEY"], api_secret=os.environ["SHOPLY_STORE_OWNER_API_SECRETE"], ) as shoply: session_response = shoply.list_sessions(limit=40) for conversation in session_response.data: print(conversation.customer_id, conversation.user_questions)

The package also provides functions for analytics, activity counts, complete session timelines, store timezone, and knowledge management. See its business-use-case guide  and API reference .

Node.js and TypeScript Package

The shoply-merchant-api-js repository provides the @shoplyai/merchant-api package for Node.js 20 or newer. It is an ESM package with built-in TypeScript declarations and no runtime dependencies. Install the pinned 2.1.3 release directly from GitHub:

npm install "github:shoplyai-public/shoply-merchant-api-js#v2.1.3"

Retrieve the same newest sessions from a Node.js server:

import { ShoplyMerchantClient } from "@shoplyai/merchant-api"; const storeKey = process.env.SHOPLY_STORE_KEY; const apiSecret = process.env.SHOPLY_STORE_OWNER_API_SECRETE; if (!storeKey || !apiSecret) { throw new Error("SHOPLY_STORE_KEY and SHOPLY_STORE_OWNER_API_SECRETE are required"); } const shoply = new ShoplyMerchantClient({ storeKey, apiSecret, }); const sessionResponse = await shoply.listSessions({ limit: 40 }); for (const conversation of sessionResponse.data) { console.log(conversation.customer_id, conversation.user_questions); }

The npm package covers the same Merchant API endpoints as the Python package. See its business-use-case guide  and API reference .

Both packages are for trusted backend services, scheduled jobs, and private internal tools. Do not import either package into storefront code or expose the API secret in a browser bundle.

Can I Call the HTTP API Without a Package?

Yes. The Merchant API remains a standard HTTPS API. The low-level Python examples below use Requests so you can see the complete wire contract or integrate from another language.

Install Requests if your environment does not already include it:

python -m pip install requests

The helper below adds the store key and authentication header to every request:

import json import os from typing import Any import requests BASE_URL = "https://api.shoplyai.ai" STORE_KEY = os.environ["SHOPLY_STORE_KEY"] STORE_OWNER_API_SECRETE = os.environ["SHOPLY_STORE_OWNER_API_SECRETE"] session = requests.Session() session.headers.update({ "Authorization": json.dumps({ "store_key": STORE_KEY, "store_owner_api_secrete": STORE_OWNER_API_SECRETE, }) }) def shoply_get(api_name: str, **params: Any) -> dict[str, Any]: """Call one store-scoped Shoply API and surface HTTP or API-level errors.""" response = session.get( f"{BASE_URL}/{api_name}", params={"store_key": STORE_KEY, **params}, timeout=30, ) response.raise_for_status() payload = response.json() if payload.get("error") is not None or payload.get("statusCode") not in (None, 200): raise RuntimeError(payload.get("error", "Shoply API request failed")) return payload def shoply_post( api_name: str, payload: dict[str, Any] | None = None, **params: Any, ) -> dict[str, Any]: """Write store-scoped data and surface HTTP or API-level errors.""" response = session.post( f"{BASE_URL}/{api_name}", params={"store_key": STORE_KEY, **params}, json=payload, timeout=60, ) response.raise_for_status() result = response.json() if result.get("error") is not None or result.get("statusCode") not in (None, 200): raise RuntimeError(result.get("error", "Shoply API request failed")) return result

The examples below use this shared client. Both helpers automatically add the authenticated store key.

Can I Manage Shoply AI Knowledge Through the API?

Yes. admin_store_knowledge uses GET to retrieve knowledge and POST to create, update, delete, or recrawl it. A POST that uses an existing knowledge ID updates that entry; using a new ID creates one.

Knowledge writes can change answers shown to shoppers. Use a dedicated secret for the system that manages knowledge, validate content before sending it, and never expose this write-capable credential in storefront JavaScript.

List Existing Knowledge

knowledge_payload = shoply_get( "admin_store_knowledge", knowledge_id_to_retrieve="_ALL_", ) for knowledge_id, knowledge in knowledge_payload["data"].items(): print(knowledge_id, knowledge.get("title"))

Sample Response

{ "data": { "knowledge::shipping-policy": { "title": "Shipping policy", "text": "Orders leave our warehouse within two business days.", "epoch_edit": 1787211246 } } }

Create or Update Custom Text Knowledge

Choose a stable, unique knowledge ID. Reuse the same ID when updating that entry.

from time import time knowledge_id = "knowledge::shipping-policy" result = shoply_post( "admin_store_knowledge", { knowledge_id: { "title": "Shipping policy", "text": ( "Orders leave our warehouse within two business days. " "Standard delivery normally takes three to five business days." ), "epoch_edit": int(time()), } }, ) print(result)

Sample Response

{ "statusCode": 200 }

A successful response means the knowledge was saved. Search indexing can take a few minutes, so a new answer may not appear immediately. If the API reports that the content was saved but indexing is delayed, confirm the stored entry with GET before retrying the write.

Add an External Webpage

Use an ID beginning with external_url::. Put the complete http:// or https:// URL in text.

from time import time result = shoply_post( "admin_store_knowledge", { "external_url::returns-policy": { "title": "Returns and exchanges", "text": "https://www.example.com/policies/returns", "epoch_edit": int(time()), } }, )

Upload and Register a Knowledge File

Knowledge files must be PDF, TXT, or Markdown and smaller than 5 MB. Upload the file first, then save the returned private S3 location as a knowledge entry.

from pathlib import Path from time import time file_path = Path("shipping-policy.md") with file_path.open("rb") as file_handle: upload_response = session.post( f"{BASE_URL}/user_generated_file", params={"store_key": STORE_KEY, "folder": "knowledge_file"}, files={"file": (file_path.name, file_handle, "text/markdown")}, timeout=60, ) upload_response.raise_for_status() uploaded = upload_response.json() if uploaded.get("statusCode") != 200: raise RuntimeError(uploaded.get("error", "Knowledge file upload failed")) shoply_post( "admin_store_knowledge", { "knowledge_file::shipping-policy": { "title": "Shipping policy document", "text": uploaded["file_url"], "epoch_edit": int(time()), } }, )

Sample Upload Response

{ "message": "File uploaded successfully", "statusCode": 200, "file_url": "s3://shoplystores/your-store.myshopify.com/uploads/shipping-policy.md" }

Treat file_url as an opaque private location returned by Shoply. Do not construct or change it yourself.

Delete Knowledge

Deletion is permanent. Retrieve the current entries first and send only the exact IDs you intend to remove.

result = shoply_post( "admin_store_knowledge", { "knowledge_ids_to_delete": [ "knowledge::shipping-policy", "external_url::returns-policy", "knowledge_file::shipping-policy", ] }, )

Request a Website Recrawl

Use a recrawl after the content on the merchant’s website changes. It is not normally necessary after every custom text update.

result = shoply_post( "admin_store_knowledge", re_crawl_ind="true", )

Business Use Cases

  • Synchronize shipping, returns, warranty, sizing, or product-care guidance from an internal content system.
  • Publish temporary campaign or holiday information without manually copying it into Shoply Admin.
  • Add a policy webpage or approved document to the knowledge base as part of a deployment workflow.
  • Remove obsolete guidance as soon as a policy or promotion ends.

Merchant API Examples

1. Retrieve Performance Analytics

admin_store_analytics powers the Analytics → Summary table. It returns sessions, chats, searches, clicks, add-to-cart events, purchases, and attributed revenue.

Endpoint

GET https://api.shoplyai.ai/admin_store_analytics

Query Parameters

ParameterRequiredDescription
store_keyYesAdded by the shared client
report_typeYesdaily, weekly, or monthly
start_dateDaily/weeklyInclusive UTC date formatted as YYYYMMDD
end_dateDaily/weeklyInclusive UTC date formatted as YYYYMMDD

Monthly reports use Shoply’s stored monthly history and do not require date bounds.

Python Example

daily_report = shoply_get( "admin_store_analytics", report_type="daily", start_date="20260801", end_date="20260820", ) for row in daily_report["data"]: print(row["date"], row["sessions"], row["purchases"], row["revenue"]) print("Totals:", daily_report["totals"]) # Weekly rows use the Monday of each week as their date label. weekly_report = shoply_get( "admin_store_analytics", report_type="weekly", start_date="20260701", end_date="20260820", ) # Monthly requests do not need start_date or end_date. monthly_report = shoply_get("admin_store_analytics", report_type="monthly")

Sample Response

{ "data": [ { "date": "20260820", "sessions": 42, "chats": 18, "searches": 24, "clicks": 11, "add_to_cart": 5, "purchases": 2, "revenue": "$168.00", "revenue_usd": "$168.00" } ], "totals": { "date": "totals", "sessions": 42, "chats": 18, "searches": 24, "clicks": 11, "add_to_cart": 5, "purchases": 2, "revenue": "$168.00", "revenue_usd": "$168.00" }, "report_type": "daily" }

Revenue can contain more than one currency. Treat revenue as display text. Use revenue_usd when you need the normalized USD value shown by Shoply.

Business Use Cases

  • Send a daily sales-impact summary to the merchant or ecommerce team.
  • Compare searches, product clicks, add-to-cart events, and purchases to find funnel drop-off.
  • Feed a weekly or monthly business-intelligence dashboard without scraping the Shoply Admin page.
  • Track attributed revenue over time and compare it with merchandising or campaign changes.

2. Retrieve the Store Timezone

admin_get_store_metadata supplies the timezone badge shown in Analytics → Chat & Search History. The page uses it to translate UTC activity into the store’s local time.

Endpoint

GET https://api.shoplyai.ai/admin_get_store_metadata

Query Parameters

ParameterRequiredDescription
store_keyYesAdded by the shared client
metadata_keyYesUse the literal value time_zone

Python Example

timezone_payload = shoply_get( "admin_get_store_metadata", metadata_key="time_zone", ) timezone = timezone_payload["data"] print(timezone["ianaTimezone"], timezone["timezoneOffsetMinutes"])

Sample Response

{ "data": { "ianaTimezone": "America/Los_Angeles", "timezoneOffset": -7, "timezoneOffsetMinutes": -420 }, "statusCode": 200 }

Business Use Cases

  • Group UTC activity into the same local calendar days that the merchant sees in Analytics.
  • Schedule daily exports after the store’s local day has ended.
  • Label reports with an unambiguous IANA timezone such as America/Los_Angeles.

Timezone offsets can change because of daylight-saving rules. Prefer ianaTimezone when your reporting system supports it instead of permanently saving one numeric offset.

3. Retrieve Activity Counts

query_api_usage_count returns store activity grouped by UTC date or the original UTC minute. It is also the source for the activity chart in Analytics → Chat & Search History.

Endpoint

GET https://api.shoplyai.ai/query_api_usage_count

Query Parameters

ParameterRequiredDescription
start_dt_utcYesUTC start time as YYYY-MM-DD HH:mm:ss
end_dt_utcYesUTC end time as YYYY-MM-DD HH:mm:ss
groupbyNoutc_date for daily totals or utc_date_time for minute rows

Python Example

activity = shoply_get( "query_api_usage_count", start_dt_utc="2026-08-01 00:00:00", end_dt_utc="2026-08-20 23:59:59", groupby="utc_date", ) for row in activity["data"]: print(row)

Sample Response

{ "data": [ { "utc_date": "2026-08-20", "QA": 18, "CHAT": 18, "SEARCH": 20, "CACHED_SEARCH": 4, "PRODUCT_SEARCH": 22, "CLICK": 11, "ADD_TO_CART": 5, "CHECKOUT": 2 } ] }

Rows include the activity counters available for that period, such as chat questions, searches, clicks, add-to-cart events, and checkout events. Counters with no activity may be absent instead of set to zero.

Business Use Cases

  • Build an hourly or daily activity chart like the one in Chat & Search History.
  • Detect unusual traffic spikes or sudden drops in customer engagement.
  • Compare product-search volume with clicks, add-to-cart events, and checkouts.
  • Estimate when support and merchandising teams should review the busiest periods.

4. Retrieve Chat and Search Session Summaries

query_sessionized_chat returns one compact record per customer session. It also powers the session list in Analytics → Chat & Search History.

Endpoint

GET https://api.shoplyai.ai/query_sessionized_chat

Query Parameters

ParameterRequiredDescription
start_dt_utcYesUTC start time, or the literal value undefined
end_dt_utcYesUTC end time, or the literal value undefined
num_sessionsNoNumber of sessions to select when either bound is undefined
ScanIndexForwardNoUse false to select the newest sessions first

Retrieve the Latest 40 Sessions

summary_payload = shoply_get( "query_sessionized_chat", start_dt_utc="undefined", end_dt_utc="undefined", num_sessions=40, ScanIndexForward="false", ) sessions = summary_payload["data"] for conversation in sessions: print(conversation["customer_id"], conversation.get("user_questions", ""))

Retrieve Sessions in a UTC Range

summary_payload = shoply_get( "query_sessionized_chat", start_dt_utc="2026-08-20 00:00:00", end_dt_utc="2026-08-20 23:59:59", ScanIndexForward="false", )

When both time bounds are present, the API returns sessions in the complete range; num_sessions does not limit that bounded query.

Sample Response

{ "data": [ { "customer_id": "customer-or-session-id", "user_questions": "Do you have this in blue?\nWhen will it ship?", "start_timestamp_ms": 1787184000000, "end_timestamp_ms": 1787184065000, "session_types": "product information, click", "customer_email": null, "customer_info": {}, "sentiment": "positive", "num_questions": 2, "num_searches": 1, "num_clicks": 1 } ] }

Optional fields vary by session. Keep customer_id, start_timestamp_ms, and end_timestamp_ms if you plan to retrieve the complete timeline.

Business Use Cases

  • Review a sample of recent conversations for answer quality and support training.
  • Find repeated customer questions that should influence product copy or FAQs.
  • Identify high-intent sessions containing searches, product clicks, or other shopping actions.
  • Export one row per session for trend analysis while avoiding the larger event-level payload.

5. Retrieve One Complete Session

Pass a selected summary to query_log_visualization to retrieve its customer, AI, search, and commerce events.

Endpoint

GET https://api.shoplyai.ai/query_log_visualization

Query Parameters

ParameterRequiredDescription
date_time_utc_startYesSession start formatted as UTC YYYY-MM-DD HH:mm:ss
date_time_utc_endYesSession end formatted as UTC YYYY-MM-DD HH:mm:ss
session_idNoThe summary’s customer_id, used for older history records
start_timestamp_msRecommendedThe summary’s exact millisecond start timestamp

Python Example

from datetime import datetime, timezone def utc_from_milliseconds(timestamp_ms: int) -> str: """Format a Shoply millisecond timestamp for the history API.""" value = datetime.fromtimestamp(timestamp_ms / 1000, tz=timezone.utc) return value.strftime("%Y-%m-%d %H:%M:%S") selected = sessions[0] timeline_payload = shoply_get( "query_log_visualization", date_time_utc_start=utc_from_milliseconds(selected["start_timestamp_ms"]), date_time_utc_end=utc_from_milliseconds(selected["end_timestamp_ms"]), session_id=selected["customer_id"], start_timestamp_ms=selected["start_timestamp_ms"], ) for event in timeline_payload["data"]: print(event.get("server_timestamp_ms"), event.get("action_type"))

Sample Response

{ "data": [ { "action_type": "QA", "session_id": "customer-or-session-id", "utc_date_time": "2026-08-20 00:00:00", "start_timestamp_ms": 1787184000000, "server_timestamp_ms": 1787184000123, "Q": { "question": "Do you have this in blue?", "question_context": {} }, "task_id": "task-id", "list_tokens": [ { "data_type": "LLMOutput", "data": "Yes. These blue options are currently available." } ], "dict_search_results": {} } ] }

The timeline can contain customer questions, AI answers, search results, product clicks, add-to-cart events, checkout events, and human-agent participation. Use server_timestamp_ms to order mixed event types. Older records may contain fewer fields.

Business Use Cases

  • Audit exactly what a customer asked and what Shoply answered during a support investigation.
  • Review answer quality together with the search results and products shown to the shopper.
  • Reconstruct the journey from question to click, add-to-cart, or checkout.
  • Diagnose one session reported by a customer without searching through unrelated history.

Can I Use One Secret for Every API?

Yes. A valid store_owner_api_secrete is scoped to one store and works across the Merchant API endpoints above. For safer operations, use separate named secrets for separate systems—for example, one for a nightly data export and another for an internal dashboard.

If one integration is retired or compromised, revoke only its secret in Shoply Settings. The other integrations will continue working.

What Should I Know About Expiry, Privacy, and Errors?

  • Store-owner API secrets expire after 90 days. Create a replacement and update the integration before its expiry date.
  • Shoply’s standard chat and search history retention period is also 90 days. Secret expiry and data retention are separate policies.
  • History responses can contain personal data, including customer identifiers, email addresses, approximate location, questions, and shopping activity. Store and process it according to your privacy policy and applicable law.
  • HTTP 403 means the secret is missing, invalid, expired, revoked, or belongs to a different store.
  • HTTP 422 usually means a required query parameter is missing or has the wrong name.
  • HTTP 500 or a response with statusCode: 500 means the request reached Shoply but could not be completed. Retry temporary failures with backoff and contact us if the error continues.
  • Use bounded UTC ranges for exports and recurring jobs so responses stay manageable.

Is Product Search Part of the Merchant API?

Shoply’s public product-search API has a different authentication and request contract. See How to Access Shoply AI Search Through the API if you want product search results for a shopper-facing experience rather than private merchant data.

For help with an integration, contact Shoply AI.