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:
| API | Category | What it returns |
|---|---|---|
admin_store_analytics | Performance | Daily, weekly, or monthly performance rows and totals |
query_api_usage_count | Engagement | Activity counts grouped by UTC minute or day |
query_sessionized_chat | Conversations | Chat and search session summaries |
query_log_visualization | Conversations | The complete event timeline for one selected session |
admin_get_store_metadata | Store configuration | The store timezone used to display UTC data correctly |
admin_store_knowledge | Knowledge | Custom text, external pages, uploaded files, deletion, and recrawling |
user_generated_file | Knowledge files | Uploads 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
403withNo access_token found, that server is still running the older access-token-only authentication code.
How Do I Create an API Secret?
- Open your Shoply AI admin.
- Go to Settings → Store owner API secret.
- Create a secret and give it a name that identifies the integration, such as
Weekly reporting export. - Copy the secret when it appears. Shoply shows the plaintext value only once.
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 requestsThe 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 resultThe 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_analyticsQuery Parameters
| Parameter | Required | Description |
|---|---|---|
store_key | Yes | Added by the shared client |
report_type | Yes | daily, weekly, or monthly |
start_date | Daily/weekly | Inclusive UTC date formatted as YYYYMMDD |
end_date | Daily/weekly | Inclusive 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_metadataQuery Parameters
| Parameter | Required | Description |
|---|---|---|
store_key | Yes | Added by the shared client |
metadata_key | Yes | Use 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_countQuery Parameters
| Parameter | Required | Description |
|---|---|---|
start_dt_utc | Yes | UTC start time as YYYY-MM-DD HH:mm:ss |
end_dt_utc | Yes | UTC end time as YYYY-MM-DD HH:mm:ss |
groupby | No | utc_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_chatQuery Parameters
| Parameter | Required | Description |
|---|---|---|
start_dt_utc | Yes | UTC start time, or the literal value undefined |
end_dt_utc | Yes | UTC end time, or the literal value undefined |
num_sessions | No | Number of sessions to select when either bound is undefined |
ScanIndexForward | No | Use 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_visualizationQuery Parameters
| Parameter | Required | Description |
|---|---|---|
date_time_utc_start | Yes | Session start formatted as UTC YYYY-MM-DD HH:mm:ss |
date_time_utc_end | Yes | Session end formatted as UTC YYYY-MM-DD HH:mm:ss |
session_id | No | The summary’s customer_id, used for older history records |
start_timestamp_ms | Recommended | The 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
403means the secret is missing, invalid, expired, revoked, or belongs to a different store. - HTTP
422usually means a required query parameter is missing or has the wrong name. - HTTP
500or a response withstatusCode: 500means 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.