REST Admin API
June 11, 2026 ยท View on GitHub
Warning: The REST Admin API is deprecated. Shopify recommends using the GraphQL Admin API for all new development. The REST API will continue to work but receives fewer updates.
This guide covers using the REST Admin API for legacy integrations and gradual migration scenarios.
Table of Contents
REST Resources
REST resources provide an ActiveRecord-like pattern for CRUD operations with dirty tracking.
Finding Resources
use shopify_sdk::{RestClient, Session, ShopDomain, AuthScopes};
use shopify_sdk::rest::{RestResource, ResourceResponse};
use shopify_sdk::rest::resources::v2026_04::Product;
// Create client
let session = Session::new(
"session-id".to_string(),
ShopDomain::new("my-store").unwrap(),
"access-token".to_string(),
"read_products".parse().unwrap(),
false,
None,
);
let client = RestClient::new(&session, None)?;
// Find a single resource
let response: ResourceResponse<Product> = Product::find(&client, 123456789, None).await?;
println!("Product: {}", response.title); // Deref to Product
// List resources
let response: ResourceResponse<Vec<Product>> = Product::all(&client, None).await?;
for product in response.iter() {
println!("- {}", product.title);
}
// Count resources
let count = Product::count(&client, None).await?;
println!("Total products: {}", count);
Creating Resources
use shopify_sdk::rest::{RestResource, TrackedResource};
use shopify_sdk::rest::resources::v2026_04::Product;
let product = Product {
id: None,
title: "New Product".to_string(),
body_html: Some("<p>Description</p>".to_string()),
vendor: Some("My Store".to_string()),
product_type: Some("Merchandise".to_string()),
..Default::default()
};
let mut tracked = TrackedResource::new(product);
let saved = tracked.save(&client).await?;
println!("Created product ID: {:?}", saved.id);
Updating Resources
use shopify_sdk::rest::{RestResource, TrackedResource, ResourceResponse};
use shopify_sdk::rest::resources::v2026_04::Product;
// Fetch existing product
let response: ResourceResponse<Product> = Product::find(&client, 123456789, None).await?;
let mut tracked = TrackedResource::from_existing(response.into_inner());
// Modify fields
tracked.title = "Updated Title".to_string();
tracked.vendor = Some("New Vendor".to_string());
// Save changes (full update)
let saved = tracked.save(&client).await?;
// Or partial update (only changed fields)
if tracked.is_dirty() {
let changes = tracked.changed_fields();
let saved = tracked.save_partial(&client, changes).await?;
tracked.mark_clean();
}
Deleting Resources
use shopify_sdk::rest::{RestResource, ResourceResponse};
use shopify_sdk::rest::resources::v2026_04::Product;
let response: ResourceResponse<Product> = Product::find(&client, 123456789, None).await?;
let product = response.into_inner();
product.delete(&client).await?;
println!("Product deleted");
REST Client
For lower-level control, use RestClient directly:
use shopify_sdk::{RestClient, Session, ShopDomain, AuthScopes};
use serde_json::json;
let client = RestClient::new(&session, None)?;
// GET request
let response = client.get("products", None).await?;
println!("Products: {:?}", response.body);
// GET with query parameters
use std::collections::HashMap;
let mut params = HashMap::new();
params.insert("limit".to_string(), "10".to_string());
params.insert("status".to_string(), "active".to_string());
let response = client.get("products", Some(¶ms)).await?;
// POST request
let body = json!({
"product": {
"title": "New Product",
"product_type": "Merchandise"
}
});
let response = client.post("products", body, None).await?;
// PUT request
let body = json!({
"product": {
"id": 123456789,
"title": "Updated Title"
}
});
let response = client.put("products/123456789", body, None).await?;
// DELETE request
client.delete("products/123456789", None).await?;
Pagination
REST responses include pagination information:
use shopify_sdk::rest::{RestResource, ResourceResponse};
use shopify_sdk::rest::resources::v2026_04::Product;
let response: ResourceResponse<Vec<Product>> = Product::all(&client, None).await?;
// Check for next page
if response.has_next_page() {
if let Some(page_info) = response.next_page_info() {
// Use page_info for next request
let mut params = std::collections::HashMap::new();
params.insert("page_info".to_string(), page_info);
params.insert("limit".to_string(), "50".to_string());
let next_page = Product::all(&client, Some(¶ms)).await?;
}
}
// Check for previous page
if response.has_prev_page() {
if let Some(page_info) = response.prev_page_info() {
// Similar handling
}
}
Iterating All Pages
use shopify_sdk::rest::{RestResource, ResourceResponse};
use shopify_sdk::rest::resources::v2026_04::Product;
use std::collections::HashMap;
let mut all_products = Vec::new();
let mut params: Option<HashMap<String, String>> = None;
loop {
let response: ResourceResponse<Vec<Product>> = Product::all(
&client,
params.as_ref()
).await?;
all_products.extend(response.into_inner());
if !response.has_next_page() {
break;
}
let mut next_params = HashMap::new();
next_params.insert(
"page_info".to_string(),
response.next_page_info().unwrap()
);
params = Some(next_params);
}
println!("Total products fetched: {}", all_products.len());
Available Resources
The SDK provides REST resources for API version 2026-04 (the v2025_10 module remains available for the previous version):
Products & Inventory
Product- ProductsVariant- Product variantsProductImage- Product imagesInventoryItem- Inventory itemsInventoryLevel- Inventory levelsLocation- Store locationsCollect- Product-collection relationships
Orders & Customers
Order- OrdersDraftOrder- Draft ordersTransaction- Order transactionsRefund- RefundsFulfillment- FulfillmentsFulfillmentOrder- Fulfillment ordersFulfillmentService- Fulfillment servicesCustomer- CustomersGiftCard- Gift cards
Collections
CustomCollection- Manual collectionsSmartCollection- Automated collections
Content
Page- Store pagesBlog- BlogsArticle- Blog articlesComment- Blog commentsTheme- Store themesAsset- Theme assetsRedirect- URL redirectsScriptTag- Script tags
Store Settings
Shop- Shop details (read-only)Policy- Store policies (read-only)Country- Shipping countriesProvince- Provinces/statesCurrency- Currencies (read-only)
Billing
ApplicationCharge- One-time app chargesRecurringApplicationCharge- Subscription chargesUsageCharge- Usage-based charges
Discounts
PriceRule- Price rulesDiscountCode- Discount codes
Access & Metadata
AccessScope- OAuth scopes (read-only)StorefrontAccessToken- Storefront tokensMetafield- MetafieldsUser- Staff accounts (read-only)Event- Store events (read-only)Webhook- Webhook subscriptions
Migration to GraphQL
We recommend migrating to GraphQL for better performance and more features.
Example: Product Query
REST:
let response = client.get("products/123456789", None).await?;
let product = &response.body["product"];
GraphQL:
use shopify_sdk::GraphqlClient;
use serde_json::json;
let client = GraphqlClient::new(&session, None);
let response = client.query(
"query { product(id: \"gid://shopify/Product/123456789\") { title } }",
None, None, None
).await?;
let product = &response.body["data"]["product"];
Example: Create Product
REST:
let body = json!({
"product": {
"title": "New Product",
"product_type": "Merchandise"
}
});
let response = client.post("products", body, None).await?;
GraphQL:
use shopify_sdk::GraphqlClient;
use serde_json::json;
let client = GraphqlClient::new(&session, None);
let response = client.query(
r#"mutation CreateProduct($input: ProductInput!) {
productCreate(input: $input) {
product { id title }
userErrors { field message }
}
}"#,
Some(json!({
"input": {
"title": "New Product",
"productType": "Merchandise"
}
})),
None, None
).await?;
Benefits of GraphQL
- Precise data fetching - Request exactly the fields you need
- Fewer requests - Get related data in a single query
- Better typing - Strong schema validation
- Bulk operations - Process large datasets asynchronously
- Subscriptions - Coming soon for real-time updates
Error Handling
use shopify_sdk::{RestClient, RestError};
match client.get("products", None).await {
Ok(response) => {
println!("Products: {:?}", response.body);
}
Err(RestError::HttpError(e)) => {
println!("HTTP error: {}", e);
}
Err(RestError::RateLimited { retry_after }) => {
println!("Rate limited, retry after {:?}", retry_after);
}
Err(e) => {
println!("Error: {}", e);
}
}
Next Steps
- GraphQL Admin API - Migrate to the recommended API
- Webhooks - Real-time event notifications