Emergency situation

In case of emergencies or breakdowns, you can send an SMS to our emergency hotline

On-call phone (SMS only)

+45 29 70 15 95

Send an SMS with the following information:

  • Your name and webshop
  • Description of the problem
  • Your callback phone number

Notes: This service is only for critical situations where your webshop is down or has serious problems. For regular support, please use our normal support channels.

REST API

Complete guide to Shoporama's REST API: authentication, all endpoints, examples and Swagger documentation.

Reading time: approx. {eight} minutes
Developer

Do you just want to manage your store without writing code?

If you don’t need to build a permanent integration but just want an AI to help you with day-to-day operations, Shoporama’s Claude integration is typically a better option. It uses the same data as the REST API but requires no coding and is secured with OAuth.

Overview

Shoporama’s REST API lets you integrate your online store with external systems, such as ERP, inventory management, PIM, CRM, and other services. The API supports CRUD (create, read, update, delete) operations on most resources in your online store.

Documentation

You can find the full API documentation, including all endpoints, parameters, and examples, in our interactive Swagger documentation:

Open the Swagger documentation

Here you can test API calls directly in your browser and view all available fields and parameters for each resource.

Get Started

  1. Go to Integrations → API Access in your Shoporama admin panel
  2. Create a new API key
  3. Select permissions for the key: All, Read-only, or Write-only. The permission applies to the entire key, not to individual resources
  4. Use the key in the Authorization header of your API calls
API-adgang under Integrationer i Shoporama-admin: tabel over API-tokens med adgangsniveau, daglig grænse, og sidst brugt, samt knappen Opret token
The Integrations → API Access page displays your API tokens along with their access level, daily call limit, and when they were last used. Here, you can create new keys for the REST API using the Create Token button.

Authentication

Add your API key to the Authorization header. You can either send the key directly or use the Bearer format:

Authorization: YOUR-API-KEY

# Or with Bearer:
Authorization: Bearer YOUR-API-KEY

Available Resources

The API provides access to the following resources. All endpoints are accessible at https://dinshop.dk/REST/:

Products and Catalog

  • /product: Products (with variants, images, prices, categories, custom fields)
  • /category: Categories
  • /brand: Brands
  • /manufacturer: Manufacturers
  • /supplier: Suppliers
  • /product-label: Product labels
  • /profile, /profile-attribute, /profile-attribute-value: Product profiles, attributes, and values
  • /product/{id}/restore: Restore a deleted product
  • /product-review: Product reviews. View, approve, hide, and delete reviews
  • /waitlist: Products with customers on the waitlist and the number of people waiting (without email addresses). The waitlist for a product can be cleared
  • /wishlist-product: Products on the most wish lists (without customer data)

Inventory

  • /stock: Inventory and entries
  • /batch: Inventory batches
  • /stock/{id}/settings: Minimum stock and cost price for a product
  • /stock-location: Inventory locations
  • /stock-move: Move inventory between locations
  • /stock-movement: Inventory movements
  • /stock-value-apply: Fill in missing cost prices on inventory entries and recalculate inventory value

Orders and Customers

  • /order: Orders
  • /order/{id}/create-label: Create a shipping label for an order
  • /order/{id}/download-label: Download shipping label
  • /order-queue: Order queue
  • /order-label: Order labels
  • /order-return: Returns
  • /order-return/{id}/received: Mark an approved return as received
  • /order-status: Change the status of up to 25 orders in a single call. With `dry_run`, nothing is changed, and the response shows what the change will trigger (e.g., emails and payment)
  • /order/{id}/pick: Record the quantities picked for an order
  • /order-pick-list: The pick list of orders waiting to be packed
  • /customer: Customers
  • /client: Guest customers who have shopped without an account
  • /customer-field: Customer fields
  • /voucher: Discount codes and gift cards

Content

  • /page: Static pages
  • /blog-post: Blog posts
  • /landing-page and /landing-page-item: Landing pages and items. GET /landing-page/{id} now returnsthe page’srules andmatch type directly in the response, so you don’t need to make a separate call to /landing-page-item
  • /menu: Menus and navigation

Newsletter

  • /newsletter-list: Newsletter lists
  • /newsletter-subscriber: Subscribers
  • /newsletter-campaign: Campaigns

Setup

  • /shipping: Shipping Methods. With PUT, you can also set free shipping, weight limits, sales tax, and customer emails for the method
  • /shipping-price: Shipping rates by country and shipping method
  • /shipping-weight-price: Adjust prices for existing weight ranges
  • /payment_gateway: Payment methods
  • /country: Countries
  • /redirect: URL redirects
  • /webhook: Webhooks
  • /webshop-settings: Webshop settings organized into the following sections: General, Product Display and Search, Cart and Checkout, Inventory, Order Emails, and Search Engines. Note that certain settings—such as currency, domain, VAT display, and login credentials—cannot be changed here

Statistics

  • /stats/{group}/{view}: Statistics reports from the admin panel covering sales, products, inventory, marketing, and customers. The figures are calculated the same way as in the admin panel, so they match for the same time period

Theme

  • /theme-settings: Theme settings for the active theme. Only settings declared in the theme’s `theme_settings.json` file can be configured
  • /theme-stylesheet: Edit parts of the store’s own stylesheet (search and replace, or append to the end) instead of overwriting the entire file
  • /theme-file: Read, create, update, and delete files in your themes. Requires that "Access to theme files" be enabled on the API key

HTTP Methods

  • GET: Retrieve a list or a single resource
  • POST: Create a new resource
  • PUT: Update an existing resource
  • DELETE: Delete a resource

Examples

Retrieve products:

curl -H "Authorization: YOUR-API-KEY" \
  https://dinshop.dk/REST/product?limit=10

Create a product:

curl -X POST -H "Authorization: YOUR-API-KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "New product", "price": 199.00}' \
  https://dinshop.dk/REST/product

Update inventory:

curl -X PUT -H "Authorization: YOUR-API-KEY" \
  -H "Content-Type: application/json" \
  -d '{"count": 50}' \
  https://dinshop.dk/REST/stock/123

The number in the path is the product ID, and ` count ` is the new total inventory.

Filtering and paging

List endpoints support the following query parameters:

  • ?limit=25: number of results per page. The upper limit depends on the endpoint (e.g., 250 for /product) and is specified in the Swagger documentation
  • ?offset=0: skip results (for paging)
  • ?search=text: free-text search
  • ?fields=name,price: limit the fields returned
  • ?last_modified=2026-01-01: only resources modified since this date

Webhooks

You can create webhooks via the API so that your system is automatically notified when changes occur. Upon creation, you’ll receive a secret used to verify webhook calls via HMAC.

Validation: What the API Rejects

To protect the store’s data, the API checks a number of fields before anything is saved. If a value is rejected, a 400 status code and an explanation are returned, and nothing from the call is saved:

  • PUT /theme-settings is validated against the active theme’s theme_settings.json. Unknown settings and invalid values for on/off, colors, and lists are rejected. Images must come from the store’s image library, and dangerous HTML, such as scripts, is removed from text fields. If the theme does not have a `theme_settings.json` file, settings cannot be configured via the API
  • PUT /country: VAT rates must be between 0 and 99.99, free shipping must be 0 or higher, and the default shipping method must be active in that country
  • The `add_label_id ` on orders must be an order label in the store
  • `order_notice_to ` in PUT /webshop may only contain email addresses of the store’s users or addresses already present in the field

When the API retrieves a file from a URL—such as a logo, favicon, PDF, product files, or images for the image library—the address must be a public HTTP or HTTPS address. A maximum of 5 redirects are allowed, and a PDF may not exceed 32 MB. An `image_id` for a logo or favicon must belong to the store. If a file does not meet the requirements, it will not be saved, so check afterward to ensure the file was successfully uploaded.

You can find the fields, limits, and error responses for each endpoint in the REST API documentation and in Swagger.

Response Codes

  • 200: Success
  • 201: Resource created
  • 204: Update/delete successful
  • 400: Invalid request
  • 401: Unauthorized (incorrect, missing, or revoked API key). For example, an OAuth key is revoked when the user who authorized it is removed from the store
  • 403: The key is valid but does not have permissions for the action (e.g., a "Read-only" key attempting to write)
  • 404: Resource not found
  • 409: Conflict with data in the store, e.g., a PUT request to /customer with an email address already in use by another customer, or a PUT request to /theme-stylesheet where the stylesheet has changed since you retrieved it. Nothing is saved
  • 429: Too many requests (rate limit)

API log: see what’s actually being called

Each API key has its own log of incoming calls. This is the tool you should use when an integration behaves differently than expected, because the log shows what actually hit your store, not what the external provider thinks is being sent.

How to find the log

  1. Go to Integrations → API Access
  2. Find the key in the list that the integration uses
  3. Click the View Log button for that key

The log always corresponds to a specific key, and there is no consolidated log across all keys. Therefore, it’s a good idea to create one key per integration with a descriptive name so you can see exactly what each service is doing. At the top of the page, you’ll see the key’s name, the key itself, and the number of entries.

The log shows

  • Time: how long ago the request was received, e.g., “3 hours ago.” Hover your mouse over it to see the exact date and time down to the second
  • Method: GET, POST, PUT, DELETE, or PATCH, displayed as a colored icon
  • Path: the endpoint that was hit, without the domain, e.g., product or order/3658
  • Parameters: the query string of the call, e.g., limit=50&offset=0. The field is empty if the call had no parameters, and very long strings are truncated
  • IP: The sender’s IP address
  • User agent: the program that made the call, e.g., a command-line tool or the integration’s own client name. Hover over it to see the full value

Sort, filter, and search

You can sort by Time, Method, Path, and IP by clicking on the column header. The most recent calls appear at the top by default. The values in the Method, Path, IP, and User agent columns are also clickable and function as shortcuts:

  • Click a method to view only, for example, all PUT calls. The filter appears at the top of the page and can be removed by clicking the X
  • Click a path to search for all calls leading up to that endpoint
  • Click an IP address to view everything that specific sender has done, or click a user agent to view all calls from the same program

The search field searches the path, parameters, IP, and user agent all at once. The number of entries at the top of the page updates based on the filter, so you can use it as a counter. You can display 25, 50, 100, or 200 entries per page, and this setting is saved for the next time you open the log.

The log does not show this

The log is an overview of incoming calls, not a full technical trace. Three items are intentionally not stored:

  • The response code: You cannot see whether a call ended with a 200 or a 404. You’ll need to check your own integration log for that
  • The content of the request: the JSON you send with POST and PUT requests is not saved. The “Parameters” column only shows what comes after the question mark in the URL
  • The response: The data returned by the API is also not stored

Calls that are rejected based on the key itself are also not included in the log. If a call is rejected with a 401 because the key is incorrect, missing, or has been deleted, it is never associated with a key, and therefore there is no entry to log. Calls that result in a 403 (the key is not authorized to perform the requested action), 404 (unknown endpoint), 405 (incorrect method), or 429 (daily limit reached), do appear in the log because the key was previously authorized.

The API log covers incoming calls to your store. If there are issues with webhooks—that is, outgoing calls from your store—they have their own log under Integrations → Webhooks.

How long are entries stored?

Entries in the API log are stored for eight weeks and then automatically deleted. This is also noted at the bottom of the log page itself. Eight weeks is plenty of time to troubleshoot an integration, but it means the log cannot be used as documentation going back several years. If you need a longer audit trail, it must be stored on the other end—that is, in the system making the call.

There’s a bonus to knowing this limit: the “Last Used” column on the API Access page is calculated based on the most recent entry in the log. If a key hasn’t been used for more than eight weeks, its last entry disappears, and the column displays a dash instead of a date. A dash therefore means “not used within the last eight weeks,” not necessarily “never used.” This is an effective way to identify old keys that can be safely deleted.

Troubleshoot an integration that isn’t behaving as expected

When an integration isn’t working as it should, the first question is always the same: Did the request even get through? The API log answers that in a matter of seconds, and its answer is independent of what the other system’s provider thinks. Go through the steps in order.

Step 1: Did the calls even get through?

Open the log for the key the integration uses, and look at the timestamps during the time period when something should have happened.

  • No entries at all: then the problem lies on the other end. Either the integration isn’t making any calls at all, or the calls are being rejected due to the key. Check that it’s configured with the correct key, the correct address for your store, and that the key hasn’t been deleted
  • Lines up to a certain point, and then nothing: the integration stopped running at that point. Note the time. This is almost always exactly what you need to find the cause in the other system.
  • Entries all the way through: the calls are coming through, and the error lies in what’s being called. Proceed to step 2

If you have multiple keys, make sure you’re looking at the right one. The log is per key, and an empty log simply means that specific key hasn’t been used.

Step 2: Do the calls use the correct path and method?

Compare the Path and Method columns with what you expect:

  • If you only see GET, the integration is reading data exclusively. If it’s also supposed to modify data, there’s a missing POST (create) or PUT (update) call
  • If you see a path that doesn’t exist in the API, the endpoint is misspelled in the configuration. Such a call will return a 404, but you can only tell this from the path, since the log doesn’t show response codes
  • If you see “product” where you expected “product/123, the integration is retrieving the entire list instead of the individual product. This isn’t necessarily an error, but it’s often the reason for high usage.

Sort by Path to group similar calls together. This makes it immediately obvious if a single endpoint accounts for the vast majority of calls.

Step 3: Is the integration sending the correct parameters?

The “Parameters” column reveals how the integration is actually configured. Typical things to look for:

  • Is there a ` last_modified ` parameter? If it’s missing, the integration fetches everything every time instead of just what has changed since the last request
  • Are `limit ` and `offset` specified, and does the `offset` increment as expected? If it stays at 0, the integration is stuck on the first page and never sees the rest of your data
  • Are there any filters you didn’t expect, such as a search or a field restriction?

Remember that the contents of POST and PUT calls aren’t saved. The log may show that an update was sent for product 123, but not what values were included in it.

Step 4: Is this the correct sender?

The IP and User Agent columns show who is using the key. If you don’t recognize the sender, the key is being used by something other than what you think—for example, an old test script, a former vendor, or a colleague who has copied the key. Click on the IP address to see everything that specific sender has done.

This is also where you’ll discover if two systems are sharing the same key. If you see two different user agents using the same key, that means two systems are using it, and you won’t be able to distinguish their usage or shut down one without affecting the other. Create a separate key for each system.

Step 5: How often does it call?

Sort by Time and look at the interval between calls. Is the synchronization running every five minutes when it should run once an hour? Is there a large cluster of calls every night? Filter by a method or search for an endpoint, and use the count at the top of the page as a reference.

If there are significantly more calls than expected, this is often the reason why the integration hits the daily limit and returns a 429 error. Read more in the article about API keys and daily limits.

For developers

The log entry is written the moment the key is authorized—that is, before the call is routed and before the daily limit is checked. This is why 403, 404, 405, and 429 errors appear in the log, while 401 never does. In other words, an empty log is in itself a useful indicator: the request either did not go through or was rejected during authentication.

Since neither the status code, request body, nor response body is stored, it’s a good idea to log the timestamp, method, path, and query string in your own client so that the two logs can be compared. At the same time, set a fixed, recognizable user agent for your calls. This is the only field in the log that you control yourself, and it makes it easy to distinguish your own script from third-party integrations, even when they share the same store.

What to Include When Contacting Support

If we’re going to look into this, it’ll go much faster if you have the following ready:

  • The name of the key used by the integration. Send the name, not the key itself
  • The exact time of a call that went wrong. Hover your mouse over the time in the log to get the date and time down to the second
  • The path and method of the call
  • What you expected to happen, and what actually happened instead

Frequently Asked Questions

I’m not technical. Do I even need to use the API log?

Not on a day-to-day basis. The log is a troubleshooting tool, not something you need to monitor constantly. But one thing you can always use it for is to check whether any calls are coming in from an integration right now. For example, if your shipping solution stops working, open the log for its key and look at the top line. If it says “2 days ago,” it’s the external service that’s stopped, not your store. That answer is often enough to get you moving forward.

Why can’t I see the response codes in the log?

The log records the incoming call, not the response. If you need to troubleshoot error codes, you’ll need to log the response in your own client. The log is used to confirm that the call was received, to see which path and method it took, and to identify the parameters it included. There’s one exception worth noting: calls rejected with a 401 status code don’t appear in the log at all, so a completely empty log is in itself a strong indication that the key is incorrect or has been deleted.

Our inventory system has stopped updating. What should I do first?

Go to Integrations → API Access, click “Log Out” for the inventory system’s key, and look at the top line. If it says “3 days ago,” the system stopped three days ago, and the error lies with the inventory system. If it says “5 minutes ago,” the calls are coming through, and you should then move on to the Path and Parameters columns. Make a note of the time before contacting the vendor, as that’s the first thing they’ll ask for.

Can I view a consolidated log for all keys in the store?

No, the log is displayed per key. That’s exactly why it’s a good practice to create one key per integration with a descriptive name, such as “Inventory System” or “Price Monitor.” On the API Access page, you can sort by “Last Used” to quickly see which keys are actually active, and the “Daily Limit” column shows usage and limits per key. If you need to compare usage across different systems, the overview is the right place to look, not the log.

Our accounting system posted an order incorrectly. Can I see in the log what was sent?

You can see that a call was sent, when, using which method, and to which path—for example, a PUT to order/3658. The actual content isn’t stored, so you can’t see which amounts or accounts were included. For that, you’ll need the attachment or the log in the accounting system. The log, however, can pinpoint the exact time, and that’s usually enough to find the correct entry on the other end.

The log shows calls every five minutes throughout the night. Is that a problem?

Not in and of itself, but it’s worth calculating. One call every five minutes adds up to just under 300 calls per day, and if the integration fetches multiple pages at a time, that number increases. This counts toward the API key’s daily limit. Look at the “Parameters” column: if there’s no “load_modified” parameter included, the integration fetches the entire catalog each time, and then the vast majority of calls are wasted. Ask the provider to switch to fetching only changes since the last request.

Can I use the API log as documentation for my accountant?

No. Entries are automatically deleted after eight weeks, and the log contains neither amounts nor the content of the calls. It documents that a system made a call to your store at a given time, not what was exchanged. For accounting documentation, you must use the orders in the store and the supporting documents in the accounting system.

Our price tracker says it updates prices every hour, but the prices aren’t changing. How do I check this?

Open the log for the price monitor’s key and click on PUT in the Method column to filter by method. If there are no PUT calls at all, the service is read-only and does not write, regardless of what is specified in their configuration. If there are PUT calls to product paths every hour, the updates will appear. First, check whether the key is set to Read-only, because in that case, write requests will be rejected with a 403 error even if the calls appear in the log. If the key is set correctly, you should investigate whether the correct products are being targeted, or if the prices are being overwritten by something else afterward.

Tip

Use the Swagger documentation to explore all endpoints and test API calls directly in the browser. It’s the easiest way to get started.

Do you need help with API integration? Contact us at support@shoporama.dk.