Contents |
The basic principle of the API is very simple. GTIN (EAN) is the unique key that's used to look up products.
You send in a list of the GTINs of the products you want to retrieve information or media from, and we return the data for any matching products.
Access to the API
We have a limited trial account you can use to test the API with in the beginning. See the Trial section.
After testing the API, you’ll need to set up your own account. To do this, please contact us and provide some basic information about yourself and your intended use of the service. Following this, you’ll need to sign a contract that outlines the terms of service and usage policies.
Once the contract is signed, you’ll receive your account details, typically on the same day. In some rare instances, a specialized API may be required. If so, please reach out to us for further details.
There are multiple versions of this API, with the latest live version being 1.84, also known as v184.
Available information
The standard API offers two primary methods for retrieving product data:
The GetProductData method returns an object containing:
GTIN
Supplier article number
Image URL
Supplier name
Brand name
Product name
Weight / volume
Content declaration
Nutrition
Product markings
Allergy information
Custom Fields
The information you can choose from in GetCustomProductData, is:
Images (Sent in the form of encrypted URLs. You can ask for images in multiple sizes/formats in each request) Product name Supplier name Brand name Content declaration Nutrition Weight / volume Product markings (currently about 35 possible markings, like Svanen, Särnär glutenfri, Nyckelhålet, Krav etc.) About 20 different article numbers (For example GTIN, DUN, Supplier article number, ICA-number, COOP-number, Servera-number etc.) Width Height Depth Allergy information Durability Recycling Storage instructions Product description Sale text Category tree connections (for suppliers) Custom Fields NutritionPerServing OrderableLevels
To optimize data lookup speed, request only the information you need.
SOAP / JSON / SSL
Our API supports both JSON and SOAP requests, responding in the same format as the request. For example, a JSON request will receive a JSON response.
SSL (https) is mandatory. Any request made to http will be redirected to https.
StringResult Get24hSecurityKey(string username, string password)
ProductResult GetProductData(string[] gtinVec, ImageThumbFormat thumbFormat, ImageThumbSize thumbSize, string langTargetmarketLimit, string secKey)
StringResult GetCustomProductDataFieldNames(string secKey)
ArbitraryProductResult GetCustomProductData(string[] gtinVec, string[] requestedDataFields, string langTargetmarketLimit, string secKey)
StringResult GetAllAvailableGTINs(string langTargetmarketLimit, string secKey)
ChangedProductGTINResult GetChangedProductsGTINs(DateTime changedAfter, string secKey)
Return types
The API will always return an object containing three variables:
XXXResult {
bool Success;
string ErrorMessage;
XXX Data;
}
Success tells you if the call was successful. If Success == false, ErrorMessage will contain a description of the error that has occurred. Data is a strongly typed variable containing hte requested data (if Success == true).
For a more detailed description of the return types, see the "Return types" chapter
Authentication
Get24hSecurityKey checks your user credentials and returns a security key valid for 24 hours. This security key should be cached in your solution and sent in evey subsequent request you make to the API.
Every time the method is called, a new key is generated.
To prevent brute force attacks and keep the logs somewhat readable, there is a limit to 50 security keys per user every 24 hours. So the recieved key needs to be cached and reused.
Available GTINs
GetAllAvailableGTINs returns a string vector with all GTINs you have access to.
Changed/Deleted products
For us and the suppliers it's of the utmost importance that all information from us, that you show to your users, is correct and updated.
GetChangedProductsGTINs returns a list of GTINs of the products that has been changed or deleted since the date/time you supply the metod with, and what kind of change it is (New/changed or deleted).
If the data is cached or stored in a database on your side, it's vital that this method is called at regular intervals to keep the data updated and accurate.
As soon as a product is marked as updated (TypeOfChange.Updated), its product data and images must be downloaded again.
If it's marked as deleted (TypeOfChange.Deleted), any product information or images you've recieved from us must be deleted.
GTIN
You supply the GetProductData and GetCustomProductData methods with a list of GTINs belonging to the products you want information about.
Only GTIN containing 8 or 13 digits are considered valid. Every sent in GTIN results in an object being returned, containing the requested data, in the same order that the GTINs were sent in. Every object contains a MatchFound property, indicating if a matching product could be found in our system or not.
To keep the responsetimes relatively low, it's not recommended to look up more that 500 GTINs in each request.
Two different methods for requesting product information
GetProductData
A simple method that you supply lith a list of the GTINs you want to look up and the type of image you want,and a product object is returned conatining a basic set of product data that would be sufficient in most situations:
GTIN Supplier article number Image URL Supplier name Brand name Product name Weight / volume Content declaration Nutrition Product markings Allergy information Custom Fields
GetCustomProductData
This method gives you the ability to customize and pick and choose exactly what information you want in each call. It gives you the ability to choose from a much wider array of data, and the ability to request for instance all images of the product (not just the main image) and even those images in several different sizes/formats in each request.
To get the "names" of the available fields, you call GetCustomProductDataFieldNames.
Images can be downloaded in three different formats:
Jpeg Png, 24 bit (with transparency) Tiff
and in several different sizes:
50 px 75 px 100 px 150 px 200 px 250 px 300 px 400 px 500 px 750 px 1000 px * 1500 px * 2000 px * Original size * * These sizes are disabled by default, but can be unlocked for your account by contacting us
GetCustomProductData
You supply GetCustomProductData with a string detailing what kind of image you want, from a number of predefined options. It's structured like "Image_Format_Size". Format is entered like Jpg, Png or Tif. The size entered like px100, px200, px500. So Image_Png_px400 would give you a link to the main product image in Png-format, 400 pixels wide and/or high. Image_Jpg_px100 would give you a link to an image thats 100 pixels wide and/or high in Jpeg format.
It's be possible to request for instance both "Image_Png_px75" and "Image_Png_px300" in GetCustomProductData, and recieve links to both images in every request.
Image & AllImages
In GetCustomProductData there are two ways to request images. Image returns a link to the primary product image.
But a product can have an unlimited number of images attached. That's why AllImages exists
If you request AllImages_Jpg_px200, it would return all images belonging to the product, with the URL to the image and some additional data about it (for example the type of image, like ProductImage or AssortmentImage), and information about angle, which side is visible primarily and so on. The additional data conatins all the information necessary for creating a GS1 file name, if that's something you need.
More information can be found in the class and structure listings at the end of the document.
Suppliers can create category trees in OPV Online, and attach products to the different categories. The API has a number of methods that allows suppliers to retrieve these trees and its attached products. Before you can collect the category tree, you need to contact us in order for you to get your MenuId, required by these functions.
Every product can be attached to any number of categories. Every category han have an infinite number of sub-categories, in an infinite depth.
MenuItemResult GetMenuTree(string language, string secKey)
MenuItemResult GetMenuSubItems(long parentMenuItemId, string language, string secKey)
ArbitraryProductResult GetMenuCustomProductData(long menuItemId, string[] requestedDataFields, string langTargetmarketLimit, string secKey)
GetMenuTree returns the entire tree at once.
GetMenuSubItems allows you to traverse the tree, by returning all the sub-categories of a certain category.
GetMenuCustomProductData works just like GetCustomProductData, but returns the product data of all products under a certain category instead of doing i GTIN lookup.
Suppliers often require specific data relevant to their business, which may not be necessary for others. For example, a cheese manufacturer might need information on where and how long the cheese has been stored, and its taste intensity—details that are irrelevant to most other suppliers.
Custom Fields address this issue by allowing suppliers to add their own fields of various types (such as textboxes, radio buttons, drop-down menus, etc.) with custom rules and validations. This enables them to enter additional product data not covered by the standard fields.
CustomFields returns all available custom fields for the current product in a simple Name-Value type of structure.
Old products might not have any custom-fields available or even only have a subset of the total set available for the company, so a Custom Field that exists on one product, might be empty or null on another.
By the end of 2014 EU regulation 1169/2011 became active. This requires anyone selling for instance food, no matter if it's online or in a store, to give the customer access to some basic information:
1 a) The name of the food
1 b) The list of ingredients
1 c) Any ingredient or processing aid listed in Annex II or derived from a substance or product listed in Annex II causing allergies or intolerances used in the manufacture or preparation of a food and still present in the finished product, even if in an altered form.
1 d) The quantity of certain ingredients or categories of ingredients.
1 e) The net quantity of the food
1 f) The date of minimum durability or the ‘use by’ date (Not required in online stores)
1 g) Any special storage conditions and/or conditions of use
1 h) The name or business name and address of the food business operator referred to in Article 8(1)
1 i) The country of origin or place of provenance where provided for in Article 26
1 j) Instructions for use where it would be difficult to make appropriate use of the food in the absence of such instructions
1 k) With respect to beverages containing more than 1,2 % by volume of alcohol, the actual alcoholic strength by volume
1 l) A nutrition declaration
All this information can be retireved through our API by calling GetCustomProductData and requesting:
SupplierName
TrademarkName
ProductName
WeightVolume (Net weight / volume)
Content
Nutrition
CompanyAddress
Storage (Instructions for storing the product)
Manual (Instructions for how to use the product)
Origin
PercentageOfAlcoholByVolume
[allergen]
Another requirement in EU 1169/2011 is that allergens are marked in bold style in all content declarations. So in many content declarations you'll see [allergen][/allergen]-tags. When writing out the content declaration, you need to do a replace to translate the [allergen] tags to a suitably styled HTML element.
Example:
Water, [allergen]flour[/allergen], yeast, [allergen]milk powder[/allergen], salt
needs to be transformed into
Water, flour, yeast, milk powder, salt
The easiest way to get familiar with how the APi works, is by calling it. So we've created a trial account that can be freely used to request data from a limited number of products.
The latest version of the API can be found here:
https://api.opv.se/WS/General/v184/ProductService.asmx
The user credentials to use with the Get24hSecurityKey-function is:
User name: WebService_TrialAccount
Password: NtcNOMZ8z0?_f2
It's the production service you're using, but with a limited account. So once you get the real user account, you only need to swtich the username and password, and you'll get access to alot more data.
We've developed a simple software that you can use to test the API: https://api.opv.se/WS/General/v184/WebServiceTester.zip The file conatins both the source code (in C#), and the runnable version (in the bin\release-folder).
To get a list of what GTINs you have access to, run the program and press the "Get all GTINs" button.
SOAP
The API describes how to call it using SOAP through the WSDL specification. Go to the API in an ordinary web browser, and click on the different methods, and you'll get a specification of how to call it and what data will be returned.
JSON
You call the different methods of the API through: https://api.opv.se/WS/General/v184/ProductService.asmx/{function_name}
For example:
https://api.opv.se/WS/General/v184/ProductService.asmx?op=Get24hSecurityKey
In the call, set dataType to "json" and contentType to "application/json; charset=utf-8".
As data, send in a JSON encoded object. For example {"username": "MyUsername", "password": "MyPassword"}
JSON code example:
$.ajax(
{
type: "POST",
dataType: "json",
contentType: 'application/json; charset=utf-8',
url: "https://api.opv.se/WS/General/v184/ProductService.asmx/GetCustomProductData",
data: "{ 'gtinVec': [ '5701211106172', '6411200106685' ],
'requestedDataFields': [ 'SupplierName', 'TrademarkName', 'ProductName', 'WeightVolume', 'Image_Jpg_px200' ],
'langTargetmarketLimit': 'sv-SE', 'secKey': '"+ secKey +"' }",
success: function (r) {
if (r == null || r.d == null) {
alert('Unknown error');
}
else if (!r.d.Success) {
alert(r.d.ErrorMessage);
}
else {
var data = r.d;
// Start processing the data
}
}
});
Note: In this example Javascript is used with jQuery to illustrate a call to the API. But because of cross site scripting-limitations, this won't work in reality. But most languages would do this in a similar way.
If you want load data from the API with Javascript, you need to create a "proxy" in the backend that passes on the call to our API and returns the data to the frontend.
Fields that can be requested through GetCustomProductData
| Field | Datatype | Information |
|---|---|---|
| AllergyInfo | AllergyInfo[] |
Returns information about allergens in the product.
Contains 0: No, 1: Trace amounts, 2: Yes. Note that allergens will me marked in the content declaration. That's where the customer should look first. These tables is to make allergens searchable, and not eveyone fills in this data. |
| AssortmentName | String | The assortment that the product belongs according to our own classification. |
| AxfoodNr | String | The Axfood article number. |
| AxfoodSnabbgrossNo | String | The Axfood Snabbgross -article number. |
| BergendahlNr | String | The Bergendahl article number. |
| Brand | BrandInfo | A BrandInfo-object, bontaining the name and our internal id of the brand. |
| ChangedDate | DateTime | The date when the products data was last changed. |
| Company | Company | The address information of the supplier (GLN, and in CompanyAddress: Name, Street address, Zip code and City). |
| Content | String |
The content declaration of the product
Contains [allergen][/allergen]-taggs around allergens. These needs to be replaced by for instance <strong>-tags to make them bold and easy for the customer to find. In the future the [allergen]-tags might contain attributes that would tell what kind of allergen it is (for example [allergen allergenId="5" allergenName="Milk"]), so it's recommended to use regular expressions when doing the replace. Something along the lines of RegEx.Replace(content, "\[allergen.*?\]", "<span class="bold" >"); |
| Cooking | String | Cooking instructions. |
| COOPNr | String | The COOP article number. |
| CustomFields | CustomFieldInfo[] | Suppliers can add their own custom fields if they feel something is missing, to extend the number of available data fields and fill in data relevant for their own users or systems. "CustomFields" returns all available custom fields for the current product. Old products might not have any custom-fields available or even only have a subset of the total set available for the company, so a Custom Field that exists on one product, might be empty or null on another. |
| DafgardNo | String | The Dafgård article number. |
| Depth | Double | The depth of the product in mm. |
| Description | String | Product description. |
| Durability | String | Information about the durability of the product (like "8 days in the refridgerator"). |
| GTIN | String | The GTIN number of the product. Might be 8, 13 or 14 digits. |
| Height | Double | The height of the product in mm. |
| ICANr | String | The ICA article number. |
| LaunchDate | DateTime | The date when the product was launched. |
| Manual | String | Instructions of how to use the product. |
| Markings | MarkInfo[] |
Official product markings, like Svanen, Nyckelhålet, Fairtrade etc.
1 Bra Miljöval/Falken * Diet type |
|
MartinAndServeraNo MartinServeraNo Martin&ServeraNo |
String | The Martin and Servera article number. |
| MenigoNo | String | The Menigo article number. |
| MenuConnections | MenuItem[] | For suppliers that use the category tree function. Returns a list of the categories the product is attached to |
| NordisktVaruNo | String | The Nordiskt Varu -article number. |
| Nutrition | String | The nutritional declaration of the product. This is always per 100 g/ml (measured). |
| NutritionSeparated | Array of NutrientQuantitySeparated |
The nutritional declaration of the product, separated so that each row is returned separated into name, amount and unit First each row is devided into a "Nutrient" and a "Quantity" ("Protein 12,7g" is divided into Nutrient: "Protein", Quantity: "12,7g"). These values are suitable if you just want to separate them into a table. But Quantity is also separated into amount and unit, that can be acessed through the "QuantitySeparated" property. In this example it would mean that you get: { Nutrient: "Protein", Quantity: "12,7g", QuantitySeparated: { Amount: 12.7, Unit: "g" } } |
| NutritionPerServing | NutritionPerServing | The nutritional declaration of the product per serving. The amount per serving varies. Returns an object containing: Nutrition (String), ServingSizeGram (Integer), Description (new!) (String), and ReferenceRecommendation (String). ReferenceRecommendation is a freetextfield that might be empty, or contain a string hinting of what type of "recommended" nutrient intake it is about, if this information is available. Maximum one NutrientPerServing per product is available. |
| NutritionPerServingSeparated | Array of NutrientQuantitySeparated |
The nutritional declaration per serving of the product, separated so that each row is returned separated into name, amount and unit First each row is devided into a "Nutrient" and a "Quantity" ("Protein 12,7g" is divided into Nutrient: "Protein", Quantity: "12,7g"). These values are suitable if you just want to separate them into a table. But Quantity is also separated into amount and unit, that can be acessed through the "QuantitySeparated" property. In this example it would mean that you get: { Nutrient: "Protein", Quantity: "12,7g", QuantitySeparated: { Amount: 12.7, Unit: "g" } } |
| OrderableLevels | Array of OrderableLevel | Array of orderable levels (often cases), which contain this GTIN/EAN. Each orderable level has GTIN, a supplier assigned number and number of units. There might be multiple orderable levels. Not all products or suppliers do have this information stored, and if no information is found the array will be empty/null. |
| Origin | String | Information about the origin of the product. |
| PercentageOfAlcoholByVolume | String | The alcohol contant of the product by volume percent. |
| ProductId | Long | The internal id we've assigned to the product. This might change when a product is updated. |
| PrivabNo | String | The PrivabNo article number. |
| ProductName | String | The name of the product. |
| Recycling | String | Instructions for recycling. |
| Saletext | String | The suppliers sale text. |
| Storage | String | Instructions for storing the product. |
| SuppArtNr | String | The suppliers own article number for the product. |
| SupplierName | String | The name of the supplier (can also be retrieved through CompanyAddress). |
| SvenskCaterNo | String | The Svensk Cater -article number. |
| SvenskSnabbmatNo | String | The Svensk snabbmat article number. |
|
BrandName TrademarkName |
String | The brand name of the product. |
| VivLogNo | String | The VivLog article number. |
| WeightVolume | String | The weight / volume of the product. |
| Width | Double | The width of the product in mm. |
|
ÖoBNo OoBNo |
String | The ÖoB (Överskottsbolaget) article number. |
| Image_[Format]_[Size] | String |
A link to the primary image in the requested format and size
See the Images section for more information. |
| AllImages_[Format]_[Size] | OPVImage[] |
Returns all images attached to the product with some additional data about each image.
See the Images section for more information. |
Result classes
class OperationResult
{
bool Success
string ErrorMessage
}
class ProductResult : OperationResult
{
Product[] Data
}
class ArbitraryProductResult : OperationResult
{
ArbitraryProduct[] Data
}
class ChangedProductGTINResult : OperationResult
{
ChangedProductGTIN[] Data
}
class StringResult : OperationResult
{
string Data
}
class StringVecResult : OperationResult
{
string[] Data;
}
public class MenuItemResult : OperationResult
{
MenuItem[] Data
}
Classes in the result Data property of the Result classes
class Product
{
bool MatchFound // If a matching GTIN could be found or not
string GTIN
string ImageURL
string Supplier
string Trademark
string Name
string WeightVolume
string SuppArtNo
string Content
string Nutrition
MarkInfo[] Markings
AllergyInfo[] Allergies
Company Company
}
class ArbitraryProduct
{
bool MatchFound // If a matching GTIN could be found or not
string GTIN
NameValue[] Data
class NameValue
{
string Name // The requested column name
object Value // The value. Might be of varying data types
}
class ChangedProductGTIN
{
public string GTIN;
public ChangeType TypeOfChange;
}
enum ChangeType
{
Updated, // The information and images should be downloded anew, and replace the existing data
Deleted // The information and images retrieved fron us should be deleted in your system
}
ArbitraryProduct.Data.Value might be of different data types. If not shown below, it will be a string:
productid long allergyinfo AllergyInfo[] markings MarkInfo[] width double height double depth double launchdate nullable DateTime changeddate DateTime company Company AllImages OPVImage[] NutritionSeparated NutrientQuantitySeparated[] CustomFields CustomFieldInfo[]
class AllergyInfo
{
int SubstanceId
string SubstanceName // Name of the substance, for instance "Gluten" or "Lupin"
ContainsAllergenic Contains // An enum indicating wheter it contains the allergen or not. No = 0, Possible = 1, Yes = 2. Possible is what often is rererred to as "Trace amounts"
string Remark // Remark from the supplier for further information
}
class MarkInfo
{
int MarkId // Our internat id of the mark
string MarkName // The name of the mark
}
class CompanyAddress
{
string Name
string Address
string Zip
string City
}
class NutritionPerServing
{
string Nutrition
int ServingSizeGram
string ReferenceRecommendation
}
class Company
{
string GLN
CompanyAddress CompanyAddress
}
class CustomFieldInfo
{
string Name // The name of the custom field
string Value // The value of the custom field
}
class NutrientQuantitySeparated
{
string Nutrient
string Quantity
string Comment
AmountAndUnit SeparatedQuantity
}
class AmountAndUnit
{
double Amount
string Unit
}
class BrandInfo
{
long BrandId
string BrandName
}
class OrderableLevel
{
string GTIN
string SupplierAssignedNumber
int NumberOfUnits
}
Classes returned when requesting AllImages from GetCustomProductData.
The letters within the parenthesis for the GDSN classes indicates the character it should be replaced with when generating a GDSN file name according to the 2013 standard
class OPVImage
{
public string OPVNo = ""; // our internal id of the iamge. Probably not somehing that's of use to you
public DBImageType ImagePurpose; // Type of image. For example Product image, environment image, assortment image etc.
public string ImageURL = ""; // The URL of the image
public GDSNImageType? ImageType = null; // Type of image according to GDSN
public GDSNImageFacing? Facing = null; // What primary side is visible
public GDSNImageAngle? Angle = null; // If it's taken from the front, or from a left or right angle
public GDSNInOutPackage? InOutPackage = null; // If it's photographed inside or out of its packaging
}
enum short DBImageType {
ProductImage
SpaceImage_front
SpaceImage_top
AssortmentImage
EnvironmentImage
SpaceImage_DF
Video
Audio
Document
SpaceImage_Side
}
enum GDSNImageType {
StillShotSingleGTIN (A)
StillShotSingleGTINWithElements (B)
StillShotSingleGTINHiRes (C)
StillShotSingleGTINWithElementsHiRes (D)
Undetermined (Z)
}
enum GDSNImageFacing {
NotApplicable (0)
Front (1)
Left (2)
Top (3)
Back (7)
Right (8)
Bottom (9)
}
enum GDSNImageAngle {
Center (C)
Left (L)
Right (R)
NoPlunge (N)
}
enum GDSNInOutPackge {
OutOfpackaging (0)
InPackaging (1)
Case_ (A)
Innerpack (B)
RawOrUncooked (C)
Prepared (D)
Plated (E)
Styled (F)
Staged (G)
Held (H)
Worn (J)
Used (K)
Family (L)
OpenCase (M)
}
MenuItemResults Every MenuItem has three properties. A MenuItemId that's unique for each category in the tree (used when calling GetMenuCustomProductData), a Name-property containing the name of the category, and a Children-property of the type MenuItem[] containing all of its child categories.
class MenuItem {
long MenuItemId
string Name
long ParentId
MenuItem[] Children
}
Note that Children always is null when calling GetMenuSubItems, since that function only fetches one level at the time.
