sellers = null;
95 |
96 | /**
97 | * Results of get and add tracking operations
98 | */
99 | public Tracking[] trackings = null;
100 |
101 | /**
102 | * Results of get and add tracking operations
103 | */
104 | public Notification[] notifications = null;
105 |
106 | /**
107 | * A list of ASINs. Result of, but not limited to, the get tracking list operation
108 | */
109 | public String[] asinList = null;
110 |
111 | /**
112 | * Estimated count of all matched products.
113 | */
114 | public Integer totalResults = null;
115 |
116 | /**
117 | * A list of sellerIds.
118 | */
119 | public String[] sellerIdList = null;
120 |
121 | /**
122 | * A list of lightning deals.
123 | */
124 | public LightningDeal[] lightningDeals = null;
125 |
126 | /**
127 | * Contains information about any error that might have occurred.
128 | */
129 | public RequestError error = null;
130 |
131 | /**
132 | * Contains request specific additional output.
133 | */
134 | public String additional = null;
135 |
136 | /**
137 | * If the reqeust failed due to an Java exception (e.g. network error), this contains the exception object.
138 | */
139 | public transient Exception exception;
140 |
141 | @Override
142 | public String toString() {
143 | if(status == KeepaAPI.ResponseStatus.OK)
144 | return gson.toJson(this);
145 | else
146 | return gsonPretty.toJson(this);
147 | }
148 |
149 | }
150 |
151 |
152 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/structs/Category.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 |
4 | import static com.keepa.api.backend.helper.Utility.gsonPretty;
5 |
6 | public final class Category {
7 |
8 | /**
9 | * Integer value for the Amazon locale this category belongs to.
10 | * {@link AmazonLocale}
11 | */
12 | public byte domainId;
13 |
14 | /**
15 | * The category node id used by Amazon. Represents the identifier of the category. Also part of the Product object's categories and rootCategory fields. Always a positive Long value.
16 | */
17 | public long catId;
18 |
19 | /**
20 | * The name of the category.
21 | */
22 | public String name;
23 |
24 | /**
25 | * The context free category name.
26 | */
27 | public String contextFreeName;
28 |
29 | /**
30 | * The websiteDisplayGroup - available for most root categories.
31 | */
32 | public String websiteDisplayGroup;
33 |
34 | /**
35 | * List of all sub categories. null or [] (empty array) if the category has no sub categories.
36 | */
37 | public long[] children;
38 |
39 | /**
40 | * The parent category's Id. Always a positive Long value. If it is 0 the category is a root category and has no parent category.
41 | */
42 | public long parent;
43 |
44 | /**
45 | * The highest (root category) sales rank we have observed of a product that is listed in this category. Note: Estimate, as the value is from the Keepa product database and not retrieved from Amazon.
46 | */
47 | public int highestRank;
48 |
49 | /**
50 | * The lowest (root category) sales rank we have observed of a product that is listed in this category. Note: Estimate, as the value is from the Keepa product database and not retrieved from Amazon.
51 | */
52 | public int lowestRank;
53 |
54 | /**
55 | * Number of products that are listed in this category. Note: Estimate, as the value is from the Keepa product database and not retrieved from Amazon.
56 | */
57 | public int productCount;
58 |
59 | /**
60 | * Determines if this category functions as a standard browse node, rather than serving promotional purposes (for example, 'Specialty Stores').
61 | */
62 | public boolean isBrowseNode;
63 |
64 | /**
65 | * Average current buy box price of all products in this category.
66 | * Value is in the currency's smallest unit (e.g., cents for USD/EUR). May be null.
67 | */
68 | public Integer avgBuyBox;
69 |
70 | /**
71 | * Average 90 day buy box price of all products in this category.
72 | * Value is in the currency's smallest unit (e.g., cents for USD/EUR). May be null.
73 | */
74 | public Integer avgBuyBox90;
75 |
76 | /**
77 | * Average 365 day buy box price of all products in this category.
78 | * Value is in the currency's smallest unit (e.g., cents for USD/EUR). May be null.
79 | */
80 | public Integer avgBuyBox365;
81 |
82 | /**
83 | * Average 30 day buy box deviation (standard deviation) of all products in this category.
84 | * Value is in the currency's smallest unit (e.g., cents for USD/EUR). May be null.
85 | */
86 | public Integer avgBuyBoxDeviation;
87 |
88 | /**
89 | * Average number of reviews of all products in this category. May be null.
90 | */
91 | public Integer avgReviewCount;
92 |
93 | /**
94 | * Average rating of all products in this category.
95 | * Value is multiplied by 10 (e.g., 45 means 4.5 stars). May be null.
96 | */
97 | public Integer avgRating;
98 |
99 | /**
100 | * Percentage of products fulfilled by Amazon (FBA) in this category. May be null.
101 | * Represents the distribution of FBA vs. third-party sellers.
102 | */
103 | public Float isFBAPercent;
104 |
105 | /**
106 | * Percentage of products sold directly by Amazon in this category. May be null.
107 | */
108 | public Float soldByAmazonPercent;
109 |
110 | /**
111 | * Percentage of products that have an active coupon in this category. May be null.
112 | */
113 | public Float hasCouponPercent;
114 |
115 | /**
116 | * Average number of new offers of all products in this category. May be null.
117 | */
118 | public Float avgOfferCountNew;
119 |
120 | /**
121 | * Average number of used offers of all products in this category. May be null.
122 | */
123 | public Float avgOfferCountUsed;
124 |
125 | /**
126 | * Number of distinct sellers with at least one active offer in this category. May be null.
127 | */
128 | public Integer sellerCount;
129 |
130 | /**
131 | * Number of distinct brands present in this category. May be null.
132 | */
133 | public Integer brandCount;
134 |
135 |
136 | @Override
137 | public String toString() {
138 | return gsonPretty.toJson(this);
139 | }
140 | }
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/structs/Deal.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 | import com.keepa.api.backend.helper.KeepaTime;
4 |
5 | import static com.keepa.api.backend.helper.Utility.gson;
6 |
7 | /**
8 | * About:
9 | * A Deal object represents a product that has recently changed (usually in price or sales rank). It contains a summary of the product and information about the changes.
10 | *
11 | * Returned by:
12 | * The Deal object is returned by the Browsing Deals request.
13 | */
14 | public class Deal {
15 |
16 | /**
17 | * The ASIN of the product
18 | */
19 | public String asin = null;
20 |
21 | /**
22 | * The parent ASIN of the product
23 | */
24 | public String parentAsin = null;
25 |
26 | /**
27 | * Title of the product. Caution: may contain HTML markup in rare cases.
28 | */
29 | public String title = null;
30 |
31 | /**
32 | * Contains the absolute difference between the current value and the average value of the respective date range interval.
33 | * The value 0 means it did not change or could not be calculated. First dimension uses the Date Range indexing, second the Price Type indexing.
34 | *
First dimension uses {@link Product.CsvType}, second domension {@link DealInterval}
35 | */
36 | public int[][] delta = null;
37 |
38 | /**
39 | * Same as {@link #delta}, but given in percent instead of absolute values.
40 | * First dimension uses {@link Product.CsvType}, second domension {@link DealInterval}
41 | */
42 | public short[][] deltaPercent = null;
43 |
44 | /**
45 | * Contains the absolute difference of the current and the previous price / rank. The value 0 means it did not change or could not be calculated.
46 | * Uses {@link Product.CsvType} indexing
47 | */
48 | public int[] deltaLast = null;
49 |
50 | /**
51 | * Contains the weighted averages in the respective date range and price type.
52 | * Note: The day interval (index 0) is actually the average of the last 48 hours, not 24 hours. This is due to the way our deals work.
53 | * First dimension uses {@link Product.CsvType}, second domension {@link DealInterval}
54 | */
55 | public int[][] avg = null;
56 |
57 | /**
58 | * Contains the prices / ranks of the product of the time we last updated it. Uses the Price Type indexing.
59 | * The price is an integer of the respective Amazon locale's smallest currency unit (e.g. euro cents or yen).
60 | * If no offer was available in the given interval (e.g. out of stock) the price has the value -1.
61 | * Shipping and Handling costs are not included. Amazon is considered to be part of the marketplace, so if
62 | * Amazon has the overall lowest new price, the marketplace new price in the corresponding time interval will
63 | * be identical to the Amazon price (except if there is only one marketplace offer).
64 | * Uses {@link Product.CsvType} indexing
65 | */
66 | public int[] current = null;
67 |
68 | /**
69 | * Category node id {@link Category#catId} of the product's root category. 0 or 9223372036854775807 if no root category known.
70 | */
71 | public long rootCat = 0L;
72 |
73 | /**
74 | * States the time this deal was found, in Keepa Time minutes.
75 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
76 | */
77 | public int creationDate = 0;
78 |
79 | /**
80 | * The name of the main product image of the product. Make sure you own the rights to use the image.
81 | * Each entry represents the integer of a US-ASCII (ISO646-US) coded character. Easiest way to convert it to a String in Javascript would be var imageName = String.fromCharCode.apply("", productObject.image);.
82 | * Example: [54,49,107,51,76,97,121,55,74,85,76,46,106,112,103], which equals "61k3Lay7JUL.jpg".
83 | * Full Amazon image path: https://m.media-amazon.com/images/I/_image name_
84 | */
85 | public byte[] image = null;
86 |
87 | /**
88 | * Array of Amazon category node ids {@link Category#catId} this product is listed in. Can be empty.
89 | * Example: [569604]
90 | */
91 | public long[] categories = null;
92 |
93 | /**
94 | * States the last time we have updated the information for this deal, in Keepa Time minutes.
95 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
96 | */
97 | public int lastUpdate = 0;
98 |
99 | /**
100 | * States the time this lightning deal is scheduled to end, in Keepa Time minutes. Only applicable to lightning deals. 0 otherwise.
101 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
102 | */
103 | public int lightningEnd = 0;
104 |
105 | /**
106 | * Limit to products with a minimum rating (A rating is an integer from 0 to 50 (e.g. 45 = 4.5 stars)).
107 | * If -1 the filter is inactive.
108 | * Example: 20 (= min. rating of 2 stars)
109 | */
110 | public int minRating = -1;
111 |
112 | /**
113 | * The {@link OfferCondition} condition of the cheapest warehouse deal of this product. Integer value:
114 | *
0 - Unknown: We were unable to determine the condition or this is not a warehouse deal in our data
115 | *
2 - Used - Like New
116 | *
3 - Used - Very Good
117 | *
4 - Used - Good
118 | *
5 - Used - Acceptable
119 | */
120 | public byte warehouseCondition;
121 |
122 | /**
123 | * The offer comment of the cheapest warehouse deal of this product. null if no warehouse deal found in our data.
124 | */
125 | public String warehouseConditionComment;
126 |
127 | /**
128 | * The timestamp indicating the starting point from which the current value has been in effect, in Keepa Time minutes.
129 | * Uses {@link Product.CsvType} indexing.
130 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
131 | */
132 | public int[] currentSince;
133 |
134 |
135 | /**
136 | * Available deal ranges.
137 | */
138 | public enum DealInterval {
139 | DAY,
140 | WEEK,
141 | MONTH,
142 | _90_DAYS;
143 | public static final DealInterval[] values = DealInterval.values();
144 | }
145 |
146 | @Override
147 | public String toString() {
148 | return gson.toJson(this);
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/structs/Tracking.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 | import com.keepa.api.backend.helper.KeepaTime;
4 |
5 | /**
6 | * Represents a Tracking Object
7 |
8 | "metaData": String,
9 | "thresholdValues": TrackingThresholdValue array,
10 | "notifyIf": TrackingNotifyIf array,
11 | "notificationType": Boolean array,
12 | "notificationCSV": Integer array,
13 | "individualNotificationInterval": Integer
14 | */
15 | public class Tracking {
16 |
17 | /**
18 | * The tracked product ASIN
19 | */
20 | public String asin = null;
21 |
22 | /**
23 | * Creation date of the tracking in {@link KeepaTime} minutes
24 | */
25 | public int createDate;
26 |
27 | /**
28 | * The time to live in hours until the tracking expires and is deleted. When setting the value through the _Add Tracking_ request it is in relation to the time of request,
29 | * when retrieving the tracking object it is relative to the `createDate`. Possible values:
30 | * any positive integer: time to live in hours
31 | * 0: never expires
32 | * any negative integer (only when setting the value):
33 | * tracking already exists: keep the original `ttl`
tracking is new: use the absolute value as `ttl`
34 | *
35 | */
36 | public int ttl;
37 |
38 | /**
39 | * Trigger a notification if tracking expires or is removed by the system (e.g. product deprecated)
40 | */
41 | public boolean expireNotify;
42 |
43 | /**
44 | * The main Amazon locale of this tracking determines the currency used for all desired prices.
45 | * Integer value for the Amazon locale {@link AmazonLocale}
46 | */
47 | public byte mainDomainId;
48 |
49 | /**
50 | * Contains all settings for price or value related tracking criteria
51 | */
52 | public TrackingThresholdValue[] thresholdValues;
53 |
54 | /**
55 | * Contains specific, meta tracking criteria, like out of stock.
56 | */
57 | public TrackingNotifyIf[] notifyIf;
58 |
59 | /**
60 | * Determines through which channels we will send notifications.
Must be a boolean array with the length of the NotificationType enum.
61 | * Uses NotificationType indexing {@link NotificationType}. True means the channel will be used.
62 | * Our Tracking API currently only supports notifications through push webhooks or API pull request. Other channels will follow soon.
63 | * Example: Only notify via API: [ false, false, false, false, false, true, false ]
64 | *
65 | * boolean[] notificationType = new boolean[Tracking.NotificationType.values.length];
66 | * notificationType[Tracking.NotificationType.API.ordinal()] = true;
67 | *
68 | */
69 | public boolean[] notificationType;
70 |
71 | /**
72 | * A history of past notifications of this tracking. Each past notification consists of 5 entries, in the format:
73 | * [{@link AmazonLocale}, {@link Product.CsvType}, {@link NotificationType}, {@link TrackingNotificationCause}, {@link KeepaTime}]
74 | */
75 | public int[] notificationCSV;
76 |
77 | /**
78 | * A tracking specific rearm timer.
79 | * -1 = use default notification timer of the user account (changeable via the website settings)
80 | * 0 = never notify a desired price more than once
81 | * larger than 0 = rearm the desired price after x minutes.
82 | */
83 | public int individualNotificationInterval;
84 |
85 | /**
86 | * Whether or not the tracking is active. A tracking is automatically deactivated if the corresponding API account is no longer sufficiently paid for.
87 | */
88 | public boolean isActive;
89 |
90 | /**
91 | * The update interval, in hours. Determines how often our system will trigger a product update. A setting of 1 hour will not trigger an update exactly every 60 minutes, but as close to that as it is efficient for our system. Throughout a day it will be updated 24 times, but the updates are not perfectly distributed.
92 | */
93 | public int updateInterval;
94 |
95 | /**
96 | * The meta data of this tracking (max length is 500 characters). Used to assign some text to this tracking, like a user reference or a memo.
97 | */
98 | public String metaData;
99 |
100 | /**
101 | * Available notification channels
102 | */
103 | public enum NotificationType {
104 | EMAIL, TWITTER, FACEBOOK_NOTIFICATION, BROWSER, FACEBOOK_MESSENGER_BOT, API, MOBILE_APP, DUMMY;
105 | public static final NotificationType[] values = NotificationType.values();
106 |
107 | }
108 |
109 | /**
110 | * The cause that triggered a notification
111 | */
112 | public enum TrackingNotificationCause {
113 | EXPIRED, DESIRED_PRICE, PRICE_CHANGE, PRICE_CHANGE_AFTER_DESIRED_PRICE, OUT_STOCK, IN_STOCK, DESIRED_PRICE_AGAIN;
114 | public static final TrackingNotificationCause[] values = TrackingNotificationCause.values();
115 | }
116 |
117 |
118 | /**
119 | * Available notification meta trigger types
120 | */
121 | public enum NotifyIfType {
122 | OUT_OF_STOCK, BACK_IN_STOCK;
123 | public static final NotifyIfType[] values = NotifyIfType.values();
124 | }
125 |
126 | /**
127 | * Represents a desired price - a {@link Product.CsvType} of a specific {@link AmazonLocale} to be monitored
128 | */
129 | public static class TrackingThresholdValue {
130 |
131 | public TrackingThresholdValue(AmazonLocale domainId, Product.CsvType csvType, int thresholdValue, boolean isDrop, Integer minDeltaAbsolute, Integer minDeltaPercentage, Boolean deltasAreBetweenNotifications){
132 | this.thresholdValue = thresholdValue;
133 | this.isDrop = isDrop;
134 | this.domain = (byte) domainId.ordinal();
135 | this.csvType = csvType.index;
136 | this.minDeltaAbsolute = minDeltaAbsolute;
137 | this.minDeltaPercentage = minDeltaPercentage;
138 | this.deltasAreBetweenNotifications = deltasAreBetweenNotifications == null ? false : deltasAreBetweenNotifications;
139 | }
140 | /**
141 | * The history of threshold values (or desired prices). Only for existing tracking!
142 | * Format: [{@link KeepaTime}, value]
143 | */
144 | public int[] thresholdValueCSV;
145 |
146 | /**
147 | * The threshold value (or desired price). Only for creating a tracking!
148 | */
149 | public int thresholdValue;
150 |
151 | /**
152 | * Integer value of the {@link AmazonLocale} this threshold value belongs to. Regardless of the locale, the threshold value is always in the currency of the mainDomainId.
153 | */
154 | public byte domain;
155 |
156 | /**
157 | * Integer value of the {@link Product.CsvType} for this threshold value
158 | */
159 | public int csvType;
160 |
161 | /**
162 | * Whether or not this tracking threshold value tracks value drops (true) or value increases (false)
163 | */
164 | public boolean isDrop;
165 |
166 | /**
167 | * not yet available.
168 | */
169 | public Integer minDeltaAbsolute;
170 |
171 | /**
172 | * not yet available.
173 | */
174 | public Integer minDeltaPercentage;
175 |
176 | /**
177 | * not yet available.
178 | */
179 | public boolean deltasAreBetweenNotifications;
180 | }
181 |
182 | public static class TrackingNotifyIf {
183 |
184 | public TrackingNotifyIf(AmazonLocale domainId, Product.CsvType csvType, NotifyIfType notifyIfType){
185 | this.domain = (byte) domainId.ordinal();
186 | this.csvType = csvType.index;
187 | this.notifyIfType = notifyIfType.ordinal();
188 | }
189 |
190 | /**
191 | * Integer value of the {@link AmazonLocale} for this NotifyIf
192 | */
193 | public byte domain;
194 |
195 | /**
196 | * The {@link Product.CsvType} for this threshold value
197 | */
198 | public int csvType;
199 |
200 | /**
201 | * The {@link NotifyIfType}
202 | */
203 | public int notifyIfType;
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/structs/Offer.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 | import static com.keepa.api.backend.helper.Utility.gson;
4 |
5 | /**
6 | * About:
7 | * The offer object represents a marketplace offer.
8 | *
9 | * Returned by:
10 | * The offer object is returned by the Product Request using the optional offers parameter and is part of the Product Object.
11 | *
12 | * Important to know:
13 | * It is impossible to update billions of marketplace offers on a regular basis. The product request's offers parameter determines how many offers we retrieve / update. We always fetch the best offers, as sorted by Amazon, in all conditions. If a product has more offers than requested, those will not be retrieved.
14 | * The order of offers constantly changes and we can retrieve a different amount of offers with each data retrieval. Because of this as well as the fact that we do keep a history of offers you will almost certainly encounter outdated offers. So the following is very important:
15 | *
16 | * Evaluate the lastSeen field - only process fresh and active offers if you are not interested in past offers.
17 | * The history of an offer (its past prices and shipping costs) is often not without gaps. Evaluate the EXTRA_INFO_UPDATES csv-type of the product object to find out when we updated the offers. If you need complete coverage of (all) offers of a product you have to request it on a regular basis.
18 | * If there are almost identical offers - same seller, same condition, same shipping type and same condition text - we only provide access to the one with the cheapest price. We do not list duplicates.
19 | */
20 | public class Offer {
21 |
22 | /**
23 | * Unique id of this offer (in the scope of the product).
24 | * Not related to the offerIds used by Amazon, as those are user specific and only valid for a short time.
25 | * The offerId can be used to identify the same offers throughout requests.
26 | *
27 | * Example: 4
28 | */
29 | public int offerId;
30 |
31 | /**
32 | * States the last time we have seen (and updated) this offer, in Keepa Time minutes.
33 | *
34 | * Example: 2700145
35 | */
36 | public int lastSeen = 0;
37 |
38 | /**
39 | * The seller id of the merchant.
40 | *
41 | * Example: A2L77EE7U53NWQ (Amazon.com Warehouse Deals)
42 | */
43 | public String sellerId = null;
44 |
45 | /**
46 | * Contains the current price and shipping costs of the offer as well as, if available, the offer's history.
47 | * It has the format Keepa time minutes, price, shipping cost, [...].
48 | *
49 | * The price and shipping cost are integers of the respective Amazon locale's smallest currency unit (e.g. euro cents or yen).
50 | * If we were unable to determine the price or shipping cost they have the value -2.
51 | * Free shipping has the shipping cost of 0.
52 | * If an offer is not shippable or has unspecified shipping costs the shipping cost will be -1.
53 | * To get the newest price and shipping cost access the last two entries of the array.
54 | * Most recent price: offerCSV[offerCSV.length - 2]
55 | * Most recent shipping cost: offerCSV[offerCSV.length - 1]
56 | */
57 | public int[] offerCSV = null;
58 |
59 | /**
60 | * The {@link OfferCondition} condition of the offered product. Integer value:
61 | *
0 - Unknown: We were unable to determine the condition.
62 | *
1 - New
63 | *
2 - Used - Like New
64 | *
3 - Used - Very Good
65 | *
4 - Used - Good
66 | *
5 - Used - Acceptable
67 | *
6 - Refurbished
68 | *
7 - Collectible - Like New
69 | *
8 - Collectible - Very Good
70 | *
9 - Collectible - Good
71 | *
10 - Collectible - Acceptable
72 | *
Note: Open Box conditions will be coded as Used conditions.
73 | */
74 | public byte condition = 0;
75 |
76 | /**
77 | * The describing text of the condition.
78 | *
79 | * Example: The item may come repackaged. Small cosmetic imperfection on top, [...]
80 | */
81 | public String conditionComment = null;
82 |
83 | /**
84 | * Whether this offer is available via Prime shipping. Can be used as a FBA ("Fulfillment by Amazon") indicator as well.
85 | */
86 | public boolean isPrime;
87 |
88 | /**
89 | * If the price of this offer is hidden on Amazon due to a MAP ("minimum advertised price") restriction.
90 | * Even if so, the offer object will contain the price and shipping costs.
91 | */
92 | public boolean isMAP;
93 |
94 | /**
95 | * Indicating whether the offer is currently shippable.
96 | * If not this could mean for example that it is temporarily out of stock or a pre-order.
97 | */
98 | public boolean isShippable;
99 |
100 | /**
101 | * Indicating whether the offer is an Add-on item.
102 | */
103 | public boolean isAddonItem;
104 |
105 | /**
106 | * Indicating whether the offer is a pre-order.
107 | */
108 | public boolean isPreorder;
109 |
110 | /**
111 | * Indicating whether the offer is a Warehouse Deal.
112 | */
113 | public boolean isWarehouseDeal;
114 |
115 | /**
116 | * Indicating whether our system identified that the offering merchant attempts to scam users.
117 | */
118 | public boolean isScam;
119 |
120 | /**
121 | * Indicating whether the offer ships from China.
122 | */
123 | public boolean shipsFromChina;
124 |
125 | /**
126 | * True if the seller is Amazon (e.g. "Amazon.com").
127 | *
128 | * Note: Amazon's Warehouse Deals seller account or other accounts Amazon is maintaining under a different name are not considered to be Amazon.
129 | */
130 | public boolean isAmazon;
131 |
132 | /**
133 | * Whether this offer is fulfilled by Amazon.
134 | */
135 | public boolean isFBA;
136 |
137 | /**
138 | * This offer has a discounted Prime exclusive price. A Prime exclusive offer can only be ordered if the buyer has an active Prime subscription.
139 | */
140 | public boolean isPrimeExcl;
141 |
142 |
143 | /**
144 | * Contains the Prime exclusive price history of this offer, if available. A Prime exclusive offer can only be ordered if the buyer has an active Prime subscription.
145 | * It has the format Keepa time minutes, price, [...].
146 | *
147 | * Most recent Prime exclusive price: primeExclCSV[primeExclCSV.length - 1]
148 | */
149 | public int[] primeExclCSV;
150 |
151 | /**
152 | * Contains the available stock of this offer as well as, if available, the stock's history.
153 | * It has the format Keepa time minutes, stock, [...].
154 | *
155 | * Most recent stock: stockCSV[stockCSV.length - 1]
156 | */
157 | public int[] stockCSV;
158 |
159 | /**
160 | * Minimum order quantity. 0 if unknown.
161 | */
162 | public int minOrderQty;
163 |
164 |
165 | /**
166 | * Contains one-time coupon details of this offer. Undefined if none is available.
167 | * Positive integer for an absolute discount or negative for a percentage discount.
168 | * Example:
169 | * 500 - Coupon with a $5 discount.
170 | * -15 - Coupon with a 15% discount.
171 | */
172 | public int coupon;
173 |
174 | /**
175 | * Contains the coupon history of this offer, if available.
176 | * It has the format Keepa time minutes, coupon, [...].
177 | */
178 | public int[] couponHistory;
179 |
180 | /**
181 | * An array that lists identical offers we detected for the same seller, condition, and shipping type that were excluded from the main offers list because they were not the cheapest.
182 | */
183 | public OfferDuplicate[] offerDuplicates;
184 |
185 |
186 | public class OfferDuplicate {
187 | public int price;
188 | public String conditionComment;
189 | public int shipping;
190 | }
191 |
192 | public enum OfferCondition {
193 | UNKNOWN(0),
194 | NEW(1),
195 | USED_NEW(2), USED_VERY_GOOD(3), USED_GOOD(4), USED_ACCEPTABLE(5),
196 | REFURBISHED(6),
197 | COLLECTIBLE_NEW(7), COLLECTIBLE_VERY_GOOD(8), COLLECTIBLE_GOOD(9), COLLECTIBLE_ACCEPTABLE(10);
198 |
199 | public final byte code;
200 | public static final OfferCondition[] values = OfferCondition.values();
201 |
202 | public static Product.CsvType getCorrespondingCsvType(OfferCondition oc) {
203 | Product.CsvType type = null;
204 | switch (oc) {
205 | case UNKNOWN:
206 | case NEW:
207 | break;
208 | case USED_NEW:
209 | type = Product.CsvType.USED_NEW_SHIPPING;
210 | break;
211 | case USED_VERY_GOOD:
212 | type = Product.CsvType.USED_VERY_GOOD_SHIPPING;
213 | break;
214 | case USED_GOOD:
215 | type = Product.CsvType.USED_GOOD_SHIPPING;
216 | break;
217 | case USED_ACCEPTABLE:
218 | type = Product.CsvType.USED_ACCEPTABLE_SHIPPING;
219 | break;
220 | case REFURBISHED:
221 | type = Product.CsvType.REFURBISHED_SHIPPING;
222 | break;
223 | case COLLECTIBLE_NEW:
224 | type = Product.CsvType.COLLECTIBLE_NEW_SHIPPING;
225 | break;
226 | case COLLECTIBLE_VERY_GOOD:
227 | type = Product.CsvType.COLLECTIBLE_VERY_GOOD_SHIPPING;
228 | break;
229 | case COLLECTIBLE_GOOD:
230 | type = Product.CsvType.COLLECTIBLE_GOOD_SHIPPING;
231 | break;
232 | case COLLECTIBLE_ACCEPTABLE:
233 | type = Product.CsvType.COLLECTIBLE_ACCEPTABLE_SHIPPING;
234 | break;
235 | }
236 |
237 | return type;
238 | }
239 |
240 | OfferCondition(int index) {
241 | code = (byte) index;
242 | }
243 | }
244 |
245 | @Override
246 | public String toString() {
247 | return gson.toJson(this);
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/KeepaAPI.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend;
2 |
3 | import com.google.gson.stream.JsonReader;
4 | import com.keepa.api.backend.helper.BasicNameFactory;
5 | import com.keepa.api.backend.structs.Request;
6 | import com.keepa.api.backend.structs.Response;
7 | import org.jdeferred.Deferred;
8 | import org.jdeferred.Promise;
9 | import org.jdeferred.impl.DeferredObject;
10 |
11 | import javax.net.ssl.HttpsURLConnection;
12 | import java.io.IOException;
13 | import java.io.InputStream;
14 | import java.io.InputStreamReader;
15 | import java.io.OutputStream;
16 | import java.net.URL;
17 | import java.nio.charset.StandardCharsets;
18 | import java.util.concurrent.ExecutorService;
19 | import java.util.concurrent.Executors;
20 | import java.util.concurrent.atomic.AtomicInteger;
21 | import java.util.zip.GZIPInputStream;
22 |
23 | import static com.keepa.api.backend.helper.Utility.gson;
24 | import static com.keepa.api.backend.helper.Utility.urlEncodeUTF8;
25 |
26 | public final class KeepaAPI {
27 | /**
28 | * Thread pool size determines degree of asynchronization.
29 | */
30 | final private ExecutorService executorDeferred;
31 | final private ExecutorService executorRetry;
32 |
33 | final private String accessKey;
34 | final private String userAgent;
35 | final private int maxDelay = 60000;
36 |
37 | public enum ResponseStatus {
38 | PENDING, OK, FAIL, NOT_ENOUGH_TOKEN, REQUEST_REJECTED, NOT_FOUND, PAYMENT_REQUIRED, METHOD_NOT_ALLOWED, INTERNAL_SERVER_ERROR
39 | }
40 |
41 | /**
42 | * @param key Your private API Access Token
43 | * @param threads Thread pool size determines degree of asynchronization. Higher thread count allows more requests in parallel to be made. Default 4
44 | */
45 | public KeepaAPI(String key, int threads) {
46 | this(key, Executors.newFixedThreadPool(threads, new BasicNameFactory("KeepaAPI-%d")), Executors.newFixedThreadPool(threads, new BasicNameFactory("KeepaAPI-RetryScheduler")));
47 | }
48 |
49 | /**
50 | * @param key Your private API Access Token
51 | * @param executorRetry provide a custom executor service for request retry attempts
52 | * @param executorDeferred provide a custom executor service for deferred request processing
53 | */
54 | public KeepaAPI(String key, ExecutorService executorRetry, ExecutorService executorDeferred) {
55 | this.accessKey = key;
56 | String apiVersion = getClass().getPackage().getImplementationVersion();
57 | if (apiVersion != null) {
58 | userAgent = "KEEPA-JAVA Framework-" + apiVersion;
59 | } else {
60 | userAgent = "KEEPA-JAVA Framework-";
61 | }
62 |
63 | this.executorDeferred = executorDeferred;
64 | this.executorRetry = executorRetry;
65 | }
66 |
67 | /**
68 | * @param key Your private API Access Token
69 | */
70 | public KeepaAPI(String key) {
71 | this(key, 4);
72 | }
73 |
74 | /**
75 | * Shutdown internal executor services (thread pools)
76 | * @param shutdownNow if true issue a shutdownNow()
77 | */
78 | public void shutdown(boolean shutdownNow) {
79 | if(shutdownNow){
80 | executorDeferred.shutdownNow();
81 | executorRetry.shutdownNow();
82 | } else {
83 | executorDeferred.shutdown();
84 | executorRetry.shutdown();
85 | }
86 | }
87 |
88 | /**
89 | * Issue a request to the Keepa Price Data API.
90 | * If your tokens are depleted, this method will fail.
91 | *
92 | * @param r the API Request {@link Request}
93 | * @return Promise for {@link Response}
94 | */
95 | public Promise sendRequest(Request r) {
96 | return sendRequest(r, 30000, 120000);
97 | }
98 |
99 | /**
100 | * Issue a request to the Keepa Price Data API.
101 | * If your tokens are depleted, this method will fail.
102 | *
103 | * @param r the API Request {@link Request}
104 | * @param connectTimeout the timeout value, in milliseconds, to be used when opening a connection to the API
105 | * @param readTimeout the read timeout value, in milliseconds, for receiving an API response
106 | * @return Promise for {@link Response}
107 | */
108 | public Promise sendRequest(Request r, int connectTimeout, int readTimeout) {
109 | Deferred d = new DeferredObject<>();
110 |
111 | if(r == null){
112 | d.reject(null);
113 | return d.promise();
114 | }
115 |
116 | executorDeferred.execute(() -> {
117 | long responseTime = System.nanoTime();
118 | Response response;
119 |
120 | String query = r.parameter.entrySet().stream()
121 | .map(p -> urlEncodeUTF8(p.getKey()) + "=" + urlEncodeUTF8(p.getValue()))
122 | .reduce((p1, p2) -> p1 + "&" + p2)
123 | .orElse("");
124 |
125 | String url = "https://api.keepa.com/" + r.path + "?key=" + accessKey + "&" + query;
126 |
127 | try {
128 | URL obj = new URL(url);
129 | HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
130 | con.setUseCaches(false);
131 | con.setRequestProperty("User-Agent", this.userAgent);
132 | con.setRequestProperty("Connection", "keep-alive");
133 | con.setRequestProperty("Accept-Encoding", "gzip");
134 | con.setConnectTimeout(connectTimeout);
135 | con.setReadTimeout(readTimeout);
136 | if (r.postData != null) {
137 | con.setRequestMethod("POST");
138 | con.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
139 | con.setDoOutput(true);
140 | try (OutputStream os = con.getOutputStream()) {
141 | os.write(r.postData.getBytes(StandardCharsets.UTF_8));
142 | }
143 | } else
144 | con.setRequestMethod("GET");
145 |
146 | int responseCode = con.getResponseCode();
147 |
148 | if (responseCode == 200) {
149 | try (InputStream is = con.getInputStream();
150 | GZIPInputStream gis = new GZIPInputStream(is)) {
151 | JsonReader reader = new JsonReader(new InputStreamReader(gis, "UTF-8"));
152 | response = gson.fromJson(reader, Response.class);
153 | response.status = ResponseStatus.OK;
154 | } catch (Exception e) {
155 | response = new Response();
156 | response.status = ResponseStatus.FAIL;
157 | response.exception = e;
158 | }
159 | } else {
160 | try (InputStream is = con.getErrorStream();
161 | GZIPInputStream gis = new GZIPInputStream(is)) {
162 | JsonReader reader = new JsonReader(new InputStreamReader(gis, "UTF-8"));
163 | response = gson.fromJson(reader, Response.class);
164 | } catch (Exception e) {
165 | response = new Response();
166 | response.status = ResponseStatus.FAIL;
167 | response.exception = e;
168 | }
169 |
170 | response.statusCode = responseCode;
171 |
172 | switch (responseCode) {
173 | case 400:
174 | response.status = ResponseStatus.REQUEST_REJECTED;
175 | break;
176 | case 402:
177 | response.status = ResponseStatus.PAYMENT_REQUIRED;
178 | break;
179 | case 404:
180 | response.status = ResponseStatus.NOT_FOUND;
181 | break;
182 | case 405:
183 | response.status = ResponseStatus.METHOD_NOT_ALLOWED;
184 | break;
185 | case 429:
186 | response.status = ResponseStatus.NOT_ENOUGH_TOKEN;
187 | break;
188 | case 500:
189 | response.status = ResponseStatus.INTERNAL_SERVER_ERROR;
190 | break;
191 | default:
192 | if (response.status != ResponseStatus.FAIL)
193 | response.status = ResponseStatus.FAIL;
194 | break;
195 | }
196 | }
197 | } catch (IOException e) {
198 | response = new Response();
199 | response.status = ResponseStatus.FAIL;
200 | response.exception = e;
201 | }
202 |
203 | response.requestTime = (System.nanoTime() - responseTime) / 1000000;
204 | if (response.status == ResponseStatus.OK)
205 | d.resolve(response);
206 | else
207 | d.reject(response);
208 | });
209 | return d.promise();
210 | }
211 |
212 | /**
213 | * Issue a request to the Keepa Price Data API.
214 | * If your API contingent is depleted, this method will retry the request as soon as there are new tokens available. May take minutes.
215 | * Will fail it the request failed too many times.
216 | *
217 | * @param r the API Request {@link Request}
218 | * @param connectTimeout the timeout value, in milliseconds, to be used when opening a connection to the API
219 | * @param readTimeout the read timeout value, in milliseconds, for receiving an API response
220 | * @return Promise for {@link Response}
221 | */
222 | public Promise sendRequestWithRetry(Request r, int connectTimeout, int readTimeout) {
223 | Deferred deferred = new DeferredObject<>();
224 | AtomicInteger expoDelay = new AtomicInteger(0);
225 |
226 | executorRetry.execute(() -> {
227 | int retry = 0;
228 | Response[] lastResponse = {null};
229 | final boolean[] solved = {false};
230 |
231 | try {
232 | int delay = 0;
233 |
234 | while (!solved[0]) {
235 | if (lastResponse[0] != null && lastResponse[0].status == ResponseStatus.NOT_ENOUGH_TOKEN && lastResponse[0].refillIn > 0)
236 | delay = lastResponse[0].refillIn + 100;
237 |
238 | if (retry > 0)
239 | Thread.sleep(delay);
240 |
241 | Promise p1 = sendRequest(r, connectTimeout, readTimeout);
242 | p1
243 | .done(result -> {
244 | deferred.resolve(result);
245 | solved[0] = true;
246 | expoDelay.set(0);
247 | })
248 | .fail(result -> {
249 | lastResponse[0] = result;
250 | switch (result.status) {
251 | case FAIL:
252 | case NOT_ENOUGH_TOKEN: // retry
253 | break;
254 | default:
255 | deferred.reject(result);
256 | solved[0] = true;
257 | }
258 | })
259 | .waitSafely();
260 |
261 | if (p1.isRejected()) {
262 | retry++;
263 | delay = expoDelay.getAndUpdate(operand -> Math.min(2 * operand + 100, maxDelay));
264 | }
265 | }
266 | } catch (InterruptedException e) {
267 | Thread.currentThread().interrupt();
268 | deferred.reject(null);
269 | }
270 | });
271 |
272 | return deferred.promise();
273 | }
274 |
275 | /**
276 | * Issue a request to the Keepa Price Data API.
277 | * If your API contingent is depleted, this method will retry the request as soon as there are new tokens available. May take minutes.
278 | * Will fail it the request failed too many times.
279 | *
280 | * @param r the API Request {@link Request}
281 | * @return Promise for {@link Response}
282 | */
283 | public Promise sendRequestWithRetry(Request r) {
284 | return sendRequestWithRetry(r, 30000, 120000);
285 | }
286 |
287 | }
288 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/structs/Seller.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 | import com.keepa.api.backend.helper.KeepaTime;
4 |
5 | import static com.keepa.api.backend.helper.Utility.gson;
6 |
7 | /**
8 | * About:
9 | * The seller object provides information about a Amazon marketplace seller.
10 | * Returned by:
11 | * The seller object is returned by the following request:
12 | * Request Seller Information
13 | */
14 | public class Seller {
15 |
16 | /**
17 | * States the time we have started tracking this seller, in Keepa Time minutes.
18 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
19 | */
20 | public int trackedSince;
21 |
22 | /**
23 | * The domainId of the products Amazon locale
24 | * {@link AmazonLocale}
25 | */
26 | public byte domainId;
27 |
28 | /**
29 | * The seller id of the merchant.
30 | *
31 | * Example: A2L77EE7U53NWQ (Amazon.com Warehouse Deals)
32 | */
33 | public String sellerId;
34 |
35 | /**
36 | * The name of seller.
37 | *
38 | * Example: Amazon Warehouse Deals
39 | */
40 | public String sellerName;
41 |
42 | /**
43 | * Two dimensional history array that contains history data for this seller. First dimension index:
44 | *
{@link MerchantCsvType}
45 | * 0 - RATING: The merchant's rating in percent, Integer from 0 to 100.
46 | * 1 - RATING_COUNT: The merchant's total rating count, Integer.
47 | */
48 | public int[][] csv;
49 |
50 | /**
51 | * States the time of our last update of this seller, in Keepa Time minutes.
52 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
53 | */
54 | public int lastUpdate;
55 |
56 | /**
57 | * Indicating whether or not our system identified that this seller attempts to scam users.
58 | */
59 | public boolean isScammer;
60 |
61 | /**
62 | * Indicating whether or not this seller ships from China.
63 | */
64 | public boolean shipsFromChina;
65 |
66 | /**
67 | * Boolean value indicating whether or not the seller currently has FBA listings.
68 | * This value is usually correct, but could be set to false even if the seller has FBA listings, since we are not always aware of all
69 | * seller listings. This can especially be the case with sellers with only a few listings consisting of slow-selling products.
70 | */
71 | public boolean hasFBA;
72 |
73 | /**
74 | * Contains the number of storefront ASINs if available and the last update of that metric.
75 | * Is null if not available (no storefront was ever retrieved). This field is available in the
76 | * default Request Seller Information (storefront parameter is not required).
77 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
78 | * Has the format: [ last update of the storefront in Keepa Time minutes, the count of storefront ASINs ]
79 | * Example: [2711319, 1200]
80 | */
81 | public int[] totalStorefrontAsins = null;
82 |
83 | /**
84 | * Only available if the storefront parameter was used and only updated if the update parameter was utilized.
85 | * String array containing up to 100,000 storefront ASINs, sorted by freshest first. The corresponding
86 | * time stamps can be found in the asinListLastSeen field.
87 | * Example: ["B00M0QVG3W", "B00M4KCH2A"]
88 | */
89 | public String[] asinList;
90 |
91 | /**
92 | * Only available if the storefront parameter was used and only updated if the update parameter was utilized.
93 | * Contains the last time (in Keepa Time minutes) we were able to verify each ASIN in the _asinList_ field.
94 | * asinList and asinListLastSeen share the same indexation, so the corresponding time stamp
95 | * for `asinList[10]` would be `asinListLastSeen[10]`.
96 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
97 | *
98 | * Example: [2711319, 2711311]
99 | */
100 | public int[] asinListLastSeen;
101 |
102 | /**
103 | * Only available if the storefront parameter was used and only updated if the update parameter was utilized.
104 | * Contains the total amount of listings of this seller. Includes historical data
105 | * asinList and asinListLastSeen share the same indexation, so the corresponding time stamp
106 | * for `asinList[10]` would be `asinListLastSeen[10]`. Has the format: Keepa Time minutes, count, ...
107 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
108 | *
109 | * Example: [2711319, 1200, 2711719, 1187]
110 | */
111 | public int[] totalStorefrontAsinsCSV;
112 |
113 | /**
114 | * Statistics about the primary categories of this seller. Based on our often incomplete and outdated product offers data.
115 | */
116 | public MerchantCategoryStatistics[] sellerCategoryStatistics = null;
117 |
118 | /**
119 | * Statistics about the primary brands of this seller. Based on our often incomplete and outdated product offers data.
120 | */
121 | public MerchantBrandStatistics[] sellerBrandStatistics = null;
122 |
123 | /**
124 | * Average number of sellers competing for the Buy Box of this seller's products (this seller included).
125 | */
126 | public float avgBuyBoxCompetitors;
127 |
128 | /**
129 | * Average New Buy Box ownership percentage
130 | */
131 | public int buyBoxNewOwnershipRate;
132 |
133 | /**
134 | * Average Used Buy Box ownership percentage
135 | */
136 | public int buyBoxUsedOwnershipRate;
137 |
138 | /**
139 | * The top five sellers most commonly offering the same products as this seller.
140 | */
141 | public Competitors[] competitors;
142 |
143 | /**
144 | * The business address. Each entry of the array contains one address line.
145 | * The last entry contains the 2 letter country code. null if not available.
146 | * Example: [123 Main Street, New York, NY, 10001, US]
147 | */
148 | public String[] address;
149 |
150 | /**
151 | * Contains up to 5 of the most recent customer feedbacks.
152 | * Each feedback object in the array contains the following fields
153 | */
154 | public FeedbackObject[] recentFeedback;
155 |
156 | /**
157 | * States the time of our last rating data update of this seller, in Keepa Time minutes.
158 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
159 | */
160 | public int lastRatingUpdate;
161 |
162 | /**
163 | * Contains the neutral percentage ratings for the last 30 days, 90 days, 365 days and lifetime, in that order.
164 | * A neutral rating is a 3 star rating.
165 | * Example: [1, 1, 1, 2]
166 | */
167 | public int[] neutralRating = null;
168 |
169 | /**
170 | * Contains the negative percentage ratings for the last 30 days, 90 days, 365 days and lifetime, in that order.
171 | * A negative rating is a 1 or 2 star rating.
172 | * Example: [3, 1, 1, 3]
173 | */
174 | public int[] negativeRating = null;
175 |
176 | /**
177 | * Contains the positive percentage ratings for the last 30 days, 90 days, 365 days and lifetime, in that order.
178 | * A positive rating is a 4 or 5 star rating.
179 | * Example: [96, 98, 98, 95]
180 | */
181 | public int[] positiveRating = null;
182 |
183 | /**
184 | * Contains the rating counts for the last 30 days, 90 days, 365 days and lifetime, in that order.
185 | * Example: [3, 10, 98, 321]
186 | */
187 | public int[] ratingCount = null;
188 |
189 | /**
190 | * The customer services address. Each entry of the array contains one address line.
191 | * The last entry contains the 2 letter country code. null if not available.
192 | * Example: [123 Main Street, New York, NY, 10001, US]
193 | */
194 | public String[] customerServicesAddress;
195 |
196 | /**
197 | * The Trade Register Number. null if not available.
198 | * Example: HRB 123 456
199 | */
200 | public String tradeNumber;
201 |
202 | /**
203 | * The business name. null if not available.
204 | * Example: Keepa GmbH
205 | */
206 | public String businessName;
207 |
208 | /**
209 | * The VAT number. null if not available.
210 | * Example: DE123456789
211 | */
212 | public String vatID;
213 |
214 | /**
215 | * The phone number. null if not available.
216 | * Example: 800 1234 567
217 | */
218 | public String phoneNumber;
219 |
220 | /**
221 | * The business type. null if not available.
222 | * Example: Unternehmen in Privatbesitz
223 | */
224 | public String businessType;
225 |
226 | /**
227 | * The share capital. null if not available.
228 | * Example: 25000
229 | */
230 | public String shareCapital;
231 |
232 | /**
233 | * The name of the business representative. null if not available.
234 | * Example: Max Mustermann
235 | */
236 | public String representative;
237 |
238 | /**
239 | * The email address of the business. null if not available.
240 | * Example: info@keepa.com
241 | */
242 | public String email;
243 |
244 |
245 | public int currentRating;
246 | public int currentRatingCount;
247 | public int ratingsLast30Days;
248 |
249 | public enum MerchantCsvType {
250 | RATING(0, false),
251 | RATING_COUNT(1, false);
252 |
253 | /**
254 | * If the values are prices.
255 | */
256 | public final boolean isPrice;
257 |
258 | public final int index;
259 | public static final MerchantCsvType[] values = MerchantCsvType.values();
260 |
261 | MerchantCsvType(int i, boolean price) {
262 | index = i;
263 | isPrice = price;
264 | }
265 |
266 | public static MerchantCsvType getCSVTypeFromIndex(int index) {
267 | for (MerchantCsvType type : MerchantCsvType.values) {
268 | if (type.index == index) return type;
269 | }
270 | return RATING;
271 | }
272 | }
273 |
274 | public static class MerchantBrandStatistics {
275 |
276 | /**
277 | * the brand (in all lower-case)
278 | */
279 | public String brand;
280 |
281 | /**
282 | * the number of products this merchant sells with this brand
283 | */
284 | public int productCount;
285 |
286 | /**
287 | * the 30 day average sales rank of these products
288 | */
289 | public int avg30SalesRank;
290 |
291 | /**
292 | * how many of these products have an Amazon offer
293 | */
294 | public int productCountWithAmazonOffer;
295 | }
296 |
297 | public static class Competitors {
298 | /**
299 | * The sellerId of the competitor, in lowercase.
300 | */
301 | public String sellerId;
302 |
303 | /**
304 | * The percentage of listings this competitor shares with the seller.
305 | */
306 | public int percent;
307 | }
308 |
309 | public static class MerchantCategoryStatistics {
310 |
311 | /**
312 | * the category id
313 | */
314 | public long catId;
315 |
316 | /**
317 | * the number of products this merchant sells with this category
318 | */
319 | public int productCount;
320 |
321 | /**
322 | * the 30 day average sales rank of these products
323 | */
324 | public int avg30SalesRank;
325 |
326 | /**
327 | * how many of these products have an Amazon offer
328 | */
329 | public int productCountWithAmazonOffer;
330 | }
331 |
332 | public static class FeedbackObject {
333 | /**
334 | * the feedback star rating - value range from 10 (1 star) to 50 (5 stars)
335 | */
336 | public int rating;
337 |
338 | /**
339 | * timestamp of the feedback, in Keepa Time minutes
340 | */
341 | public int date;
342 |
343 | /**
344 | * the feedback text
345 | */
346 | public String feedback = null;
347 |
348 | /**
349 | * whether or not the feedback is striked
350 | */
351 | public boolean isStriked;
352 | }
353 |
354 | @Override
355 | public String toString() {
356 | return gson.toJson(this);
357 | }
358 | }
359 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/structs/DealRequest.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 | import static com.keepa.api.backend.helper.Utility.gsonPretty;
4 |
5 | /**
6 | * Required by the Browsing Deals request.
7 | * The queryJSON contains all request parameters. It must be URL encoded if the GET format is used. To quickly get a valid queryJSON there is a link on the deals page26 below the filters that generates this JSON for the current selection.
8 | */
9 | public class DealRequest {
10 |
11 | /**
12 | * Most deal queries have more than 150 results (which is the maximum page size). To browse all deals found by a query (which is cached for 120 seconds) you iterate the page parameter and keep all other parameters identical. You start with page 0 and stop when the response contains less than 150 results.
13 | */
14 | public int page;
15 |
16 | /**
17 | * The domainId of the products Amazon locale {@link AmazonLocale}
18 | */
19 | public int domainId;
20 |
21 | /**
22 | * Determines the type of the deal. Even though it is an integer array, it can contain only one entry. Multiple types per query are not yet supported.
23 | * Uses {@link Product.CsvType} coding. Only those applicable with {@link Product.CsvType#isDealRelevant} set to true.
24 | */
25 | public int[] priceTypes;
26 |
27 | /**
28 | * Include only products for which the specified price type is at its lowest value (since tracking began).
29 | */
30 | public boolean isLowest;
31 |
32 | /**
33 | * Include only products for which the specified price type is at its lowest value in the past 90 days.
34 | */
35 | public boolean isLowest90;
36 |
37 | /**
38 | * Include only products if the selected price type is the lowest of all New offers (applicable to Amazon and Marketplace New).
39 | */
40 | public boolean isLowestOffer;
41 |
42 | /**
43 | * Our deals are devided in different sets, determined by the time interval in which the product changed. The shorter the interval, the more recent the change; which is good for big price drops but bad for slow incremental drops that accumulate over a longer period.
44 | * For most deals the shorter intervals can be considered as subsets of the longer intervals. To find more deals use the longer intervals.
45 | * Possible values:
46 | *
47 | * - 0 - day: the last 24 hours
48 | * - 1 - week: the last 24 * 7 hours
49 | * - 2 - month: the last 24 * 31 hours
50 | * - 3 - 90 days: the last 24 * 90 hours
51 | *
52 | */
53 | public int dateRange;
54 |
55 | /**
56 | * Limit the range of the absolute difference between the current value and the one at the beginning of the chosen dateRange interval.
57 | * Example: [0,999] (= no minimum difference, maximum difference of $9.99).
58 | */
59 | public int[] deltaRange;
60 |
61 | /**
62 | * Used to exclude products that are listed in these categories. If it is a sub category the product must be directly listed in this category. It will not exclude products in child categories of the specified ones, unless it is a root category.
63 | * Array with category node ids14.
64 | */
65 | public long[] excludeCategories;
66 |
67 | /**
68 | * Used to only include products that are listed in these categories. If it is a sub category the product must be directly listed in this category. It will not include products in child categories of the specified ones, unless it is a root category. Array with category node ids14.
69 | */
70 | public long[] includeCategories;
71 |
72 | /**
73 | * Same as deltaRange, but given in percent instead of absolute values. Minimum percent is 10, for Sales Rank it is 80.
74 | * Example: [30,80] (= between 30% and 80%).
75 | */
76 | public int[] deltaPercentRange;
77 |
78 | /**
79 | * Limit the range of the current value of the price type.
80 | * Example: [105,50000] (= minimum price $1.05, maximum price $500, or a rank between 105 and 50000).
81 | */
82 | public int[] currentRange;
83 |
84 | /**
85 | * Select deals by a keywords based product title search. The search is case-insensitive and supports multiple keywords which, if specified and separated by a space, must all match.
86 | * Examples:
87 | * "samsung galaxy" matches all products which have the character sequences "samsung" AND "galaxy" within their title
88 | */
89 | public String titleSearch;
90 |
91 | /**
92 | * Only include products which were out of stock within the last 24 hours and can now be ordered.
93 | */
94 | public boolean isBackInStock;
95 |
96 | /**
97 | * Only include products which were available to order within the last 24 hours and now out of stock.
98 | */
99 | public boolean isOutOfStock;
100 |
101 | /**
102 | * Switch to enable the range options.
103 | */
104 | public boolean isRangeEnabled;
105 |
106 | /**
107 | * Excludes all products that are listed as adult items.
108 | */
109 | public boolean filterErotic;
110 |
111 | /**
112 | * Determines the sort order of the retrieved deals. To invert the sort order use negative values.
113 | * Possible sort by values:
114 | *
115 | * - 1 - deal age. Newest deals first, not invertible.
116 | * - 2 - absolute delta. (the difference between the current value and the one at the beginning of the chosen dateRange interval). Sort order is highest delta to lowest.
117 | * - 3 - sales rank. Sort order is lowest rank o highest.
118 | * - 4 - percentage delta (the percentage difference between the current value and the one at the beginning of the chosen dateRange interval). Sort order is highest percent to lowest.
119 | *
120 | */
121 | public int sortType;
122 |
123 | /**
124 | * Limit the Sales Rank range of the product. Identical to currentRange if price type is set to Sales Rank. If you want to keep the upper bound open, you can specify -1 (which will translate to the maximum signed integer value).
125 | * Important note: Once this range option is used all products with no Sales Rank will be excluded. Set it to null to not use it.
126 | * Examples:
127 | * [0,5000] (= only products which have a sales rank between 0 and 5000)
128 | * [5000,-1] (= higher than 5000)
129 | */
130 | public int[] salesRankRange;
131 |
132 | /**
133 | * Switch to enable the filter options.
134 | */
135 | public boolean isFilterEnabled;
136 |
137 | /**
138 | * Limit the range of the absolute difference between the current value and previous one.
139 | * Example: [100,500] (= last change between one $1 and $5)
140 | */
141 | public int[] deltaLastRange;
142 |
143 | /**
144 | * If true excludes all products with no reviews. If false the filter is inactive.
145 | */
146 | public boolean hasReviews;
147 |
148 | /**
149 | * Include only products flagged as Prime Exclusive.
150 | * Example: {@code true}
151 | */
152 | public boolean isPrimeExclusive;
153 |
154 | /**
155 | * Include only products that currently have an offer sold and fulfilled by Amazon.
156 | * Example: {@code true}
157 | */
158 | public boolean mustHaveAmazonOffer;
159 |
160 | /**
161 | * Include only products that currently have no offer sold and fulfilled by Amazon.
162 | * Example: {@code true}
163 | */
164 | public boolean mustNotHaveAmazonOffer;
165 |
166 | /**
167 | * Minimum product rating to include.
168 | *
169 | * - Integer from 0 to 50 (e.g., 45 = 4.5 stars)
170 | * - Use -1 to disable the filter
171 | *
172 | * Example: {@code 20} // ≥ 2.0 stars
173 | */
174 | public Integer minRating;
175 |
176 | /**
177 | * Include only Amazon Warehouse deals that match these condition codes.
178 | * Use integer-coded conditions such as:
179 | * 1 = New, 2 = Used - Like New, 3 = Used - Very Good, 24 = Used - Good, 5 = Used - Acceptable.
180 | * Example: {@code [1, 2]}
181 | * Note: API expects integers; choose a numeric type that fits your model.
182 | */
183 | public Byte[] warehouseConditions;
184 |
185 | /**
186 | * If multiple variations match, return only a single (random) variation.
187 | * Example: {@code true}
188 | */
189 | public Boolean singleVariation;
190 |
191 | /**
192 | * Include only products made of the specified materials (e.g., "cotton").
193 | * Example: {@code ["cotton", "polyester"]}
194 | */
195 | public String[] material;
196 |
197 | /**
198 | * Include only products matching the specified type (e.g., "shirt", "dress").
199 | * Example: {@code ["shirt"]}
200 | */
201 | public String[] type;
202 |
203 | /**
204 | * Include only products from the specified manufacturer.
205 | * Example: {@code ["Sony"]}
206 | */
207 | public String[] manufacturer;
208 |
209 | /**
210 | * Include only products from the specified brand.
211 | * Example: {@code ["Apple", "Samsung"]}
212 | */
213 | public String[] brand;
214 |
215 | /**
216 | * Include only products in the specified Amazon product group (e.g., "home", "book").
217 | * Example: {@code ["book"]}
218 | */
219 | public String[] productGroup;
220 |
221 | /**
222 | * Include only products matching the specified model identifier.
223 | * Example: {@code ["A2179"]}
224 | */
225 | public String[] model;
226 |
227 | /**
228 | * Include only products matching the specified color attribute.
229 | * Example: {@code ["black", "navy"]}
230 | */
231 | public String[] color;
232 |
233 | /**
234 | * Include only products matching the specified size (e.g., "small", "one size").
235 | * Example: {@code ["M", "L"]}
236 | */
237 | public String[] size;
238 |
239 | /**
240 | * Include only products with the specified unit type (e.g., "count", "ounce").
241 | * Example: {@code ["count"]}
242 | */
243 | public String[] unitType;
244 |
245 | /**
246 | * Include only products with the specified scent (e.g., "lavender", "citrus").
247 | * Example: {@code ["lavender"]}
248 | */
249 | public String[] scent;
250 |
251 | /**
252 | * Include only products matching the specified item form (e.g., "liquid", "sheet").
253 | * Example: {@code ["liquid"]}
254 | */
255 | public String[] itemForm;
256 |
257 | /**
258 | * Include only products matching the specified pattern (e.g., "striped", "solid").
259 | * Example: {@code ["striped"]}
260 | */
261 | public String[] pattern;
262 |
263 | /**
264 | * Include only products matching the specified style attribute (e.g., "modern", "vintage").
265 | * Example: {@code ["modern"]}
266 | */
267 | public String[] style;
268 |
269 | /**
270 | * Include only products matching the specified item type keyword (custom search term).
271 | * Example: {@code ["books", "prints"]}
272 | */
273 | public String[] itemTypeKeyword;
274 |
275 | /**
276 | * Include only products targeting the specified audience (e.g., "kids", "professional").
277 | * Example: {@code ["kids"]}
278 | */
279 | public String[] targetAudienceKeyword;
280 |
281 | /**
282 | * Include only products matching the specified edition (e.g., "first edition", "standard edition").
283 | * Example: {@code ["first edition"]}
284 | */
285 | public String[] edition;
286 |
287 | /**
288 | * Include only products in the specified format (e.g., "kindle ebook", "import", "dvd").
289 | * Example: {@code ["paperback"]}
290 | */
291 | public String[] format;
292 |
293 | /**
294 | * Include only products by the specified author (applicable to books, music, etc.).
295 | * Example: {@code ["Neil Gaiman"]}
296 | */
297 | public String[] author;
298 |
299 | /**
300 | * Include only products with the specified binding type (e.g., "paperback").
301 | * Example: {@code ["hardcover"]}
302 | */
303 | public String[] binding;
304 |
305 | /**
306 | * Include only products available in the specified languages (use language names).
307 | * Example: {@code ["English", "German"]}
308 | */
309 | public String[] languages;
310 |
311 | /**
312 | * Include only products sold under the specified Brand Store name on Amazon.
313 | * Example: {@code ["Amazon Basics"]}
314 | */
315 | public String[] brandStoreName;
316 |
317 | /**
318 | * Include only products sold under the specified URL-friendly Brand Store identifier.
319 | * Example: {@code ["amazonbasics"]}
320 | */
321 | public String[] brandStoreUrlName;
322 |
323 | /**
324 | * Include only products in the specified website display group.
325 | * Example: {@code ["fashion_display_on_website"]}
326 | */
327 | public String[] websiteDisplayGroup;
328 |
329 | /**
330 | * Include only products in the specified website display group name (user-friendly label).
331 | * Example: {@code ["Fashion"]}
332 | */
333 | public String[] websiteDisplayGroupName;
334 |
335 | /**
336 | * Include only products belonging to the specified sales rank display group
337 | * (e.g., "fashion_display_on_website").
338 | * Example: {@code ["fashion_display_on_website"]}
339 | */
340 | public String[] salesRankDisplayGroup;
341 |
342 |
343 | @Override
344 | public String toString() {
345 | return gsonPretty.toJson(this);
346 | }
347 | }
348 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/helper/ProductAnalyzer.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.helper;
2 |
3 | import static com.keepa.api.backend.structs.Product.CsvType;
4 |
5 | /**
6 | * Provides methods to work on the Keepa price history CSV format.
7 | */
8 | public class ProductAnalyzer {
9 |
10 | /**
11 | * finds the extreme point in the specified interval
12 | *
13 | * @param csv value/price history csv
14 | * @param start start of the interval (keepa time minutes), can be 0.
15 | * @param end end of the interval (keepa time minutes), can be in the future (Integer.MAX_VALUE).
16 | * @param type the type of the csv data. If the csv includes shipping costs the extreme point will be the landing price (price + shipping).
17 | * @return extremePoints (time, lowest value/price, time, highest value/price) in the given interval or -1 if no extreme point was found. If the csv includes shipping costs it will be the landing price (price + shipping).
18 | */
19 | public static int[] getExtremePointsInIntervalWithTime(int[] csv, int start, int end, CsvType type) {
20 | if (csv == null || start >= end || csv.length < (type.isWithShipping ? 6 : 4))
21 | return new int[]{-1, -1, -1, -1};
22 |
23 | int[] extremeValue = new int[]{-1, Integer.MAX_VALUE, -1, -1};
24 |
25 | int lastTime = getLastTime(csv, type);
26 | int firstTime = csv[0];
27 | if (lastTime == -1 || firstTime == -1 || firstTime > end) return new int[]{-1, -1, -1, -1};
28 |
29 | if (firstTime > start)
30 | start = firstTime;
31 |
32 | int loopIncrement = (type.isWithShipping ? 3 : 2);
33 | int adjustedIndex = type.isWithShipping ? 2 : 1;
34 |
35 | for (int i = 1, j = csv.length; i < j; i += loopIncrement) {
36 | int c = csv[i];
37 | int date = csv[i - 1];
38 | if (date >= end)
39 | break;
40 |
41 | if (c != -1) {
42 | if (type.isWithShipping) {
43 | int s = csv[i + 1];
44 | c += s < 0 ? 0 : s;
45 | }
46 |
47 | if (date >= start) {
48 | if (c < extremeValue[1]) {
49 | extremeValue[1] = c;
50 | extremeValue[0] = csv[i - 1];
51 | }
52 |
53 | if (c > extremeValue[3]) {
54 | extremeValue[3] = c;
55 | extremeValue[2] = csv[i - 1];
56 | }
57 | } else {
58 | boolean isValid = false;
59 | if (i == j - adjustedIndex) {
60 | isValid = true;
61 | } else {
62 | int nextDate = csv[i + adjustedIndex];
63 | if (nextDate >= end || (nextDate >= start))
64 | isValid = true;
65 | }
66 |
67 | if (isValid) {
68 | if (c < extremeValue[1]) {
69 | extremeValue[1] = c;
70 | extremeValue[0] = start;
71 | }
72 |
73 | if (c > extremeValue[3]) {
74 | extremeValue[3] = c;
75 | extremeValue[2] = start;
76 | }
77 | }
78 | }
79 | }
80 | }
81 |
82 | if (extremeValue[1] == Integer.MAX_VALUE) return new int[]{-1, -1, -1, -1};
83 | return extremeValue;
84 | }
85 |
86 | /**
87 | * Get the last value/price change.
88 | *
89 | * @param csv value/price history csv
90 | * @param type the type of the csv data. If the csv includes shipping costs the extreme point will be the landing price (price + shipping).
91 | * @return the last value/price change delta. If the csv includes shipping costs it will be the delta of the the landing prices (price + shipping).
92 | */
93 | private static int getDeltaLast(int[] csv, CsvType type) {
94 | if (type.isWithShipping) {
95 | if (csv == null || csv.length < 6 || csv[csv.length - 1] == -1 || csv[csv.length - 5] == -1)
96 | return 0;
97 |
98 | int v = csv[csv.length - 5];
99 | int s = csv[csv.length - 4];
100 | int totalLast = v < 0 ? v : v + (s < 0 ? 0 : s);
101 |
102 | v = csv[csv.length - 2];
103 | s = csv[csv.length - 1];
104 | int totalCurrent = v < 0 ? v : v + (s < 0 ? 0 : s);
105 |
106 | return totalCurrent - totalLast;
107 | } else {
108 | if (csv == null || csv.length < 4 || csv[csv.length - 1] == -1 || csv[csv.length - 3] == -1)
109 | return 0;
110 |
111 | return csv[csv.length - 1] - csv[csv.length - 3];
112 | }
113 | }
114 |
115 | /**
116 | * Get the last value/price.
117 | *
118 | * @param csv value/price history csv
119 | * @param type the type of the csv data.
120 | * @return the last value/price. If the csv includes shipping costs it will be the landing price (price + shipping).
121 | */
122 | public static int getLast(int[] csv, CsvType type) {
123 | if (csv == null || csv.length == 0) return -1;
124 |
125 | if (type.isWithShipping) {
126 | int s = csv[csv.length - 1];
127 | int v = csv[csv.length - 2];
128 | return v < 0 ? v : v + (s < 0 ? 0 : s);
129 | }
130 |
131 | return csv[csv.length - 1];
132 | }
133 |
134 | /**
135 | * Get the time (keepa time minutes) of the last entry. This does not correspond to the last update time, but to the last time we registered a price/value change.
136 | *
137 | * @param csv value/price history csv
138 | * @param type the type of the csv data.
139 | * @return keepa time minutes of the last entry
140 | */
141 | public static int getLastTime(int[] csv, CsvType type) {
142 | return csv == null || csv.length == 0 ? -1 : csv[csv.length - (type.isWithShipping ? 3 : 2)];
143 | }
144 |
145 | /**
146 | * Get the value/price at the specified time
147 | *
148 | * @param csv value/price history csv
149 | * @param time value/price lookup time (keepa time minutes)
150 | * @param type the type of the csv data.
151 | * @return the price or value of the product at the specified time. -1 if no value was found or if the product was out of stock. If the csv includes shipping costs it will be the landing price (price + shipping).
152 | */
153 | public static int getValueAtTime(int[] csv, int time, CsvType type) {
154 | if (csv == null || csv.length == 0) return -1;
155 | int i = 0;
156 |
157 | int loopIncrement = (type.isWithShipping ? 3 : 2);
158 | for (; i < csv.length; i += loopIncrement)
159 | if (csv[i] > time) break;
160 |
161 | if (i > csv.length) return getLast(csv, type);
162 | if (i < loopIncrement) return -1;
163 |
164 | if (type.isWithShipping) {
165 | int v = csv[i - 2];
166 | int s = csv[i - 1];
167 | return v < 0 ? v : v + (s < 0 ? 0 : s);
168 | }
169 |
170 | return csv[i - 1];
171 | }
172 |
173 | /**
174 | * Get the price and shipping cost at the specified time
175 | *
176 | * @param csv price with shipping history csv
177 | * @param time price lookup time (keepa time minutes)
178 | * @return int[price, shipping] - the price and shipping cost of the product at the specified time. [-1, -1] if no price was found or if the product was out of stock.
179 | */
180 | public static int[] getPriceAndShippingAtTime(int[] csv, int time) {
181 | if (csv == null || csv.length == 0) return new int[]{-1, -1};
182 | int i = 0;
183 |
184 | for (; i < csv.length; i += 3) {
185 | if (csv[i] > time) {
186 | break;
187 | }
188 | }
189 |
190 | if (i > csv.length) return getLastPriceAndShipping(csv);
191 | if (i < 3) return new int[]{-1, -1};
192 |
193 | return new int[]{csv[i - 2], csv[i - 1]};
194 | }
195 |
196 |
197 | /**
198 | * Get the last price and shipping cost.
199 | *
200 | * @param csv price with shipping history csv
201 | * @return int[price, shipping] - the last price and shipping cost.
202 | */
203 | public static int[] getLastPriceAndShipping(int[] csv) {
204 | if (csv == null || csv.length < 3) return new int[]{-1, -1};
205 | return new int[]{csv[csv.length - 2], csv[csv.length - 1]};
206 | }
207 |
208 |
209 | /**
210 | * @param csv value/price history csv
211 | * @param time time to begin the search
212 | * @param type the type of the csv data.
213 | * @return the closest value/price found to the specified time. If the csv includes shipping costs it will be the landing price (price + shipping).
214 | */
215 | public static int getClosestValueAtTime(int[] csv, int time, CsvType type) {
216 | if (csv == null || csv.length == 0) return -1;
217 | int i = 0;
218 | int loopIncrement = (type.isWithShipping ? 3 : 2);
219 | for (; i < csv.length; i += loopIncrement)
220 | if (csv[i] > time) break;
221 |
222 | if (i > csv.length) return getLast(csv, type);
223 | if (i < loopIncrement) {
224 | if (type.isWithShipping) {
225 | if (csv.length < 4) {
226 | int v = csv[2];
227 | int s = csv[1];
228 | return v < 0 ? v : v + (s < 0 ? 0 : s);
229 | } else
230 | i += 3;
231 | } else {
232 | if (csv.length < 3)
233 | return csv[1];
234 | else
235 | i += 2;
236 | }
237 | }
238 |
239 | if (type.isWithShipping) {
240 | if (csv[i - 2] != -1) {
241 | int v = csv[i - 2];
242 | int s = csv[i - 1];
243 | return v < 0 ? v : v + (s < 0 ? 0 : s);
244 | } else {
245 | for (; i < csv.length; i += loopIncrement) {
246 | if (csv[i - 2] != -1) break;
247 | }
248 | if (i > csv.length) return getLast(csv, type);
249 | if (i < 3) return -1;
250 | int v = csv[i - 2];
251 | int s = csv[i - 1];
252 | return v < 0 ? v : v + (s < 0 ? 0 : s);
253 | }
254 | } else {
255 | if (csv[i - 1] != -1)
256 | return csv[i - 1];
257 | else {
258 | for (; i < csv.length; i += 2) {
259 | if (csv[i - 1] != -1) break;
260 | }
261 | if (i > csv.length) return getLast(csv, type);
262 | if (i < 2) return -1;
263 | return csv[i - 1];
264 | }
265 | }
266 | }
267 |
268 |
269 | /**
270 | * finds the lowest and highest value/price of the csv history
271 | *
272 | * @param csv value/price history csv
273 | * @param type the type of the csv data.
274 | * @return [0] = low, [1] = high. If the csv includes shipping costs the extreme point will be the landing price (price + shipping). [-1, -1] if insufficient data.
275 | */
276 | public static int[] getLowestAndHighest(int[] csv, CsvType type) {
277 | int[] minMax = getExtremePointsInIntervalWithTime(csv, 0, Integer.MAX_VALUE, type);
278 | return new int[]{minMax[1], minMax[3]};
279 | }
280 |
281 | /**
282 | * finds the lowest and highest value/price of the csv history including the dates of the occurrences (in keepa time minutes).
283 | *
284 | * @param csv value/price history csv
285 | * @param type the type of the csv data.
286 | * @return [0] = low time, [1] = low, [2] = high time, [3] = high. If the csv includes shipping costs the extreme point will be the landing price (price + shipping). [-1, -1, -1, -1] if insufficient data.
287 | */
288 | public static int[] getLowestAndHighestWithTime(int[] csv, CsvType type) {
289 | return getExtremePointsInIntervalWithTime(csv, 0, Integer.MAX_VALUE, type);
290 | }
291 |
292 | /**
293 | * Returns a weighted mean of the products csv history in the last X days
294 | *
295 | * @param csv value/price history csv
296 | * @param now current keepa time minutes
297 | * @param days number of days the weighted mean will be calculated for (e.g. 90 days, 60 days, 30 days)
298 | * @param type the type of the csv data.
299 | * @return the weighted mean or -1 if insufficient history csv length (less than a day). If the csv includes shipping costs it will be the wieghted mean of the landing price (price + shipping).
300 | */
301 | public static int calcWeightedMean(int[] csv, int now, double days, CsvType type) {
302 | return getWeightedMeanInInterval(csv, now, now - (int) (days * 24 * 60), now, type);
303 | }
304 |
305 | public static int getWeightedMeanInInterval(int[] v, int now, int start, int end, CsvType type) {
306 | long avg = -1;
307 | if (start >= end || v == null || v.length == 0) return -1;
308 |
309 | int size = v.length;
310 | int loopIncrement = type.isWithShipping ? 3 : 2;
311 |
312 | int lastTime = getLastTime(v, type);
313 | int firstTime = v[0];
314 |
315 | if (lastTime == -1 || firstTime == -1 || firstTime > end) return -1;
316 |
317 | long count = 0;
318 |
319 | if (firstTime > start) start = firstTime;
320 | if (end > now) end = now;
321 |
322 | int adjustedIndex = type.isWithShipping ? 2 : 1;
323 | for (int i = 1; i < size; i += loopIncrement) {
324 | int date = v[i - 1];
325 | if (date >= end) break;
326 |
327 | int c = v[i];
328 | if (c < 0) continue;
329 |
330 | if (type.isWithShipping) {
331 | int s = v[i + 1];
332 | c += Math.max(s, 0);
333 | }
334 |
335 | if (date >= start) {
336 | if (i == 1 && i + adjustedIndex == size)
337 | return c;
338 |
339 | int nextDate = (i + adjustedIndex == size) ? now : v[i + adjustedIndex];
340 | if (nextDate > end) nextDate = end;
341 |
342 | long tmpCount = nextDate - date;
343 | count += tmpCount;
344 | avg += c * tmpCount;
345 | } else {
346 | if (i == size - adjustedIndex || v[i + adjustedIndex] >= end)
347 | return c;
348 |
349 | int nextDate = v[i + adjustedIndex];
350 | if (nextDate >= start) {
351 | count = nextDate - start;
352 | avg = c * count;
353 | }
354 | }
355 | }
356 |
357 | if (avg > -1)
358 | avg = count != 0 ? (int) Math.floor(avg / (double) count) : -1;
359 |
360 | return (int) avg;
361 | }
362 |
363 | /**
364 | * Returns true if the CSV was out of stock in the given period.
365 | *
366 | * @param csv value/price history csv
367 | * @param start start of the interval (keepa time minutes), can be 0.
368 | * @param end end of the interval (keepa time minutes), can be in the future (Integer.MAX_VALUE).
369 | * @param type the type of the csv data.
370 | * @return was out of stock in interval, null if the csv is too short to tell.
371 | */
372 | public static Boolean getOutOfStockInInterval(int[] csv, int start, int end, CsvType type) {
373 | if (type.isWithShipping) {
374 | if (csv == null || csv.length < 6)
375 | return null;
376 | } else if (start >= end || csv == null || csv.length < 4)
377 | return null;
378 |
379 | int loopIncrement = (type.isWithShipping ? 3 : 2);
380 | for (int i = 0; i < csv.length; i += loopIncrement) {
381 | int date = csv[i];
382 | if (date <= start) continue;
383 | if (date >= end) break;
384 | if (csv[i + 1] == -1) return true;
385 | }
386 |
387 | return false;
388 | }
389 |
390 | /**
391 | * Returns a the percentage of time in the given interval the price type was out of stock
392 | *
393 | * @param csv value/price history csv
394 | * @param now current keepa time minutes
395 | * @param start start of the interval (keepa time minutes), can be 0.
396 | * @param end end of the interval (keepa time minutes), can be in the future (Integer.MAX_VALUE).
397 | * @param type the type of the csv data.
398 | * @param trackingSince the product object's trackingSince value
399 | * @return percentage between 0 and 100 or -1 if insufficient data. 100 = 100% out of stock in the interval.
400 | */
401 | public static int getOutOfStockPercentageInInterval(int[] csv, int now, int start, int end, CsvType type, int trackingSince) {
402 | if (!type.isPrice) return -1;
403 | if (start >= end) return -1;
404 | if (csv == null || csv.length == 0)
405 | return -1;
406 |
407 | int size = csv.length;
408 | int loopIncrement = (type.isWithShipping ? 3 : 2);
409 |
410 | int lastTime = getLastTime(csv, type);
411 | int firstTime = csv[0];
412 |
413 | if (lastTime == -1 || firstTime == -1 || firstTime > end || trackingSince > end) return -1;
414 |
415 | long count = 0;
416 |
417 | if (trackingSince > start)
418 | start = trackingSince;
419 |
420 | if (end > now)
421 | end = now;
422 |
423 | int adjustedIndex = type.isWithShipping ? 2 : 1;
424 |
425 | for (int i = 1, j = size; i < j; i += loopIncrement) {
426 | int c = csv[i];
427 | int date = csv[i - 1];
428 |
429 | if (date >= end)
430 | break;
431 |
432 | if (c != -1) {
433 | if (date >= start) {
434 | if (i == 1) {
435 | if (i + adjustedIndex == j) {
436 | return 0;
437 | }
438 | }
439 |
440 | int nextDate;
441 | if (i + adjustedIndex == j) {
442 | nextDate = Math.min(now, end);
443 | } else {
444 | nextDate = csv[i + adjustedIndex];
445 | if (nextDate > end)
446 | nextDate = end;
447 | }
448 |
449 | long tmpCount = nextDate - date;
450 |
451 | count += tmpCount;
452 | } else {
453 | if (i == j - adjustedIndex) {
454 | return 0;
455 | } else {
456 | int nextDate = csv[i + adjustedIndex];
457 |
458 | if (nextDate >= end)
459 | return 0;
460 |
461 | if (nextDate >= start)
462 | count = nextDate - start;
463 | }
464 | }
465 | }
466 | }
467 |
468 | if (count > 0)
469 | count = 100 - (int) Math.floor((count * 100) / (double) (end - start));
470 | else if (count == 0) {
471 | count = 100;
472 | }
473 |
474 | return (int) count;
475 | }
476 | }
477 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/structs/Stats.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 | import com.keepa.api.backend.helper.KeepaTime;
4 |
5 | import java.util.LinkedHashMap;
6 |
7 | import static com.keepa.api.backend.helper.Utility.gson;
8 |
9 | /**
10 | * Contains statistic values.
11 | * Only set if the stats parameter was used in the Product Request. Part of the {@link Product}
12 | */
13 | public class Stats {
14 |
15 | /**
16 | * Contains the prices / ranks of the product of the time we last updated it.
17 | * Uses {@link Product.CsvType} indexing.
18 | * The price is an integer of the respective Amazon locale's smallest currency unit (e.g. euro cents or yen).
19 | * If no offer was available in the given interval (e.g. out of stock) the price has the value -1.
20 | */
21 | public int[] current = null;
22 |
23 | /**
24 | * Contains the weighted mean for the interval specified in the product request's stats parameter.
25 | * Uses {@link Product.CsvType} indexing.
26 | * If no offer was available in the given interval or there is insufficient data it has the value -1.
27 | */
28 | public int[] avg = null;
29 |
30 |
31 | /**
32 | * Contains the weighted mean for the last 30 days.
33 | * Uses {@link Product.CsvType} indexing.
34 | * If no offer was available in the given interval or there is insufficient data it has the value -1.
35 | */
36 | public int[] avg30 = null;
37 |
38 |
39 | /**
40 | * Contains the weighted mean for the last 90 days.
41 | * Uses {@link Product.CsvType} indexing.
42 | * If no offer was available in the given interval or there is insufficient data it has the value -1.
43 | */
44 | public int[] avg90 = null;
45 |
46 | /**
47 | * Contains the weighted mean for the last 180 days.
48 | * Uses {@link Product.CsvType} indexing.
49 | * If no offer was available in the given interval or there is insufficient data it has the value -1.
50 | */
51 | public int[] avg180 = null;
52 |
53 | /**
54 | * Contains the weighted mean for the last 365 days.
55 | * Uses {@link Product.CsvType} indexing.
56 | * If no offer was available in the given interval or there is insufficient data it has the value -1.
57 | */
58 | public int[] avg365 = null;
59 |
60 | /**
61 | * Contains the prices registered at the start of the interval specified in the product request's stats parameter.
62 | * Uses {@link Product.CsvType} indexing.
63 | * If no offer was available in the given interval or there is insufficient data it has the value -1.
64 | */
65 | public int[] atIntervalStart = null;
66 |
67 | /**
68 | * Contains the lowest prices registered for this product.
69 | * First dimension uses {@link Product.CsvType} indexing
70 | * Second dimension is either null, if there is no data available for the price type, or
71 | * an array of the size 2 with the first value being the time of the extreme point (in Keepa time minutes) and the second one the respective extreme value.
72 | *
73 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
74 | */
75 | public int[][] min = null;
76 |
77 | /**
78 | * Contains the lowest prices registered in the interval specified in the product request's stats parameter.
79 | * First dimension uses {@link Product.CsvType} indexing
80 | * Second dimension is either null, if there is no data available for the price type, or
81 | * an array of the size 2 with the first value being the time of the extreme point (in Keepa time minutes) and the second one the respective extreme value.
82 | *
83 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
84 | */
85 | public int[][] minInInterval = null;
86 |
87 | /**
88 | * Contains the highest prices registered for this product.
89 | * First dimension uses {@link Product.CsvType} indexing
90 | * Second dimension is either null, if there is no data available for the price type, or
91 | * an array of the size 2 with the first value being the time of the extreme point (in Keepa time minutes) and the second one the respective extreme value.
92 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
93 | */
94 | public int[][] max = null;
95 |
96 | /**
97 | * Contains the highest prices registered in the interval specified in the product request's stats parameter.
98 | * First dimension uses {@link Product.CsvType} indexing
99 | * Second dimension is either null, if there is no data available for the price type, or
100 | * an array of the size 2 with the first value being the time of the extreme point (in Keepa time minutes) and the second one the respective extreme value.
101 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
102 | */
103 | public int[][] maxInInterval = null;
104 |
105 | /**
106 | * Whether the current price is the all-time lowest price.
107 | * Uses {@link Product.CsvType} indexing
108 | */
109 | public boolean[] isLowest = null;
110 |
111 | /**
112 | * Whether the current price is the lowest price in the last 90 days.
113 | * Uses {@link Product.CsvType} indexing
114 | */
115 | public boolean[] isLowest90 = null;
116 |
117 | /**
118 | * Number of times in the last 30 days Amazon went out of stock.
119 | */
120 | public Integer outOfStockCountAmazon30 = null;
121 |
122 | /**
123 | * Number of times in the last 90 days Amazon went out of stock.
124 | */
125 | public Integer outOfStockCountAmazon90 = null;
126 |
127 | /**
128 | * Contains the difference in percent between the current monthlySold value and the average value of the last 90 days.
129 | * The value 0 means it did not change or could not be calculated.
130 | */
131 | public Short deltaPercent90_monthlySold = null;
132 |
133 | /**
134 | *
135 | * Contains the out of stock percentage in the interval specified in the product request's stats parameter.
136 | * Uses {@link Product.CsvType} indexing.
137 | * It has the value -1 if there is insufficient data or the CsvType is not a price.
138 | * Examples: 0 = never was out of stock, 100 = was out of stock 100% of the time, 25 = was out of stock 25% of the time
139 | */
140 | public int[] outOfStockPercentageInInterval = null;
141 |
142 | /**
143 | * Contains the 90 day out of stock percentage
144 | * Uses {@link Product.CsvType} indexing.
145 | * It has the value -1 if there is insufficient data or the CsvType is not a price.
146 | * Examples: 0 = never was out of stock, 100 = was out of stock 100% of the time, 25 = was out of stock 25% of the time
147 | */
148 | public int[] outOfStockPercentage90 = null;
149 |
150 | /**
151 | * Contains the 30 day out of stock percentage
152 | * Uses {@link Product.CsvType} indexing.
153 | * It has the value -1 if there is insufficient data or the CsvType is not a price.
154 | * Examples: 0 = never was out of stock, 100 = was out of stock 100% of the time, 25 = was out of stock 25% of the time
155 | */
156 | public int[] outOfStockPercentage30 = null;
157 |
158 | /**
159 | * Can be used to identify past, upcoming and current lightning deal offers.
160 | * Has the format [startDate, endDate] (if not null, always array length 2). *null* if the product never had a lightning deal. Both timestamps are in UTC and Keepa time minutes.
161 | * If there is a upcoming lightning deal, only startDate is be set (endDate has value -1)
162 | * If there is a current lightning deal, both startDate and endDate will be set. startDate will be older than the current time, but endDate will be a future date.
163 | * If there is only a past deal, both startDate and endDate will be set in the past.
164 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
165 | */
166 | public int[] lightningDealInfo = null; // [startDate, endDate], or null
167 |
168 | /**
169 | * the total count of offers this product has (all conditions combined). The offer count per condition can be found in the current field.
170 | */
171 | public int totalOfferCount = -2;
172 |
173 | /**
174 | * the last time the offers information was updated. Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
175 | */
176 | public int lastOffersUpdate = -1;
177 |
178 | /**
179 | * Contains the total stock available per item condition (of the retrieved offers) for 3rd party FBA
180 | * (fulfillment by Amazon, Warehouse Deals included) or FBM (fulfillment by merchant) offers. Uses the {@link Offer.OfferCondition} indexing.
181 | */
182 | public int[] stockPerCondition3rdFBA = null;
183 |
184 | /**
185 | * Contains the total stock available per item condition (of the retrieved offers) for 3rd party FBM
186 | * (fulfillment by Amazon, Warehouse Deals included) or FBM (fulfillment by merchant) offers. Uses the {@link Offer.OfferCondition} indexing.
187 | */
188 | public int[] stockPerConditionFBM = null;
189 |
190 | /**
191 | * Only set when the offers parameter was used. The stock of Amazon, if Amazon has an offer. Max. reported stock is 10. Otherwise -2.
192 | */
193 | public int stockAmazon = -2;
194 |
195 | /**
196 | * Only set when the offers parameter was used. The stock of buy box offer. Max. reported stock is 10. If the boy box is empty/unqualified: -2.
197 | */
198 | public int stockBuyBox = -2;
199 |
200 | /**
201 | * Only set when the offers parameter was used. The count of actually retrieved offers for this request.
202 | */
203 | public int retrievedOfferCount = -2;
204 |
205 | /**
206 | * Only set when the offers parameter was used. The buy box price, if existent. Otherwise -2.
207 | */
208 | public int buyBoxPrice = -2;
209 |
210 | /**
211 | * Only set when the offers parameter was used. The buy box shipping cost, if existent. Otherwise -2.
212 | */
213 | public int buyBoxShipping = -2;
214 |
215 | /**
216 | * Only set when the offers parameter was used. Whether or not a seller won the buy box. If there are only sellers with bad offers none qualifies for the buy box.
217 | */
218 | public Boolean buyBoxIsUnqualified = null;
219 |
220 | /**
221 | * Only set when the offers parameter was used. Whether or not the buy box is listed as being shippable.
222 | */
223 | public Boolean buyBoxIsShippable = null;
224 |
225 | /**
226 | * Only set when the offers parameter was used. If the buy box is a pre-order.
227 | */
228 | public Boolean buyBoxIsPreorder = null;
229 |
230 | /**
231 | * Only set when the offers parameter was used. Whether or not the buy box is fulfilled by Amazon.
232 | */
233 | public Boolean buyBoxIsFBA = null;
234 |
235 | /**
236 | * The last time the Buy Box price was updated. Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
237 | */
238 | public Integer lastBuyBoxUpdate = null;
239 |
240 | /**
241 | * Only set when the offers parameter was used. Whether or not there is a used buy box offer.
242 | */
243 | public Boolean buyBoxIsUsed = null;
244 |
245 | /**
246 | * Whether the buy box offer is back-ordered
247 | */
248 | public Boolean buyBoxIsBackorder = null;
249 |
250 | /**
251 | * Only set when the offers parameter was used. If Amazon is the seller in the buy box.
252 | */
253 | public Boolean buyBoxIsAmazon = null;
254 |
255 | /**
256 | * Only set when the offers parameter was used. If the buy box price is hidden on Amazon due to MAP restrictions (minimum advertised price).
257 | */
258 | public Boolean buyBoxIsMAP = null;
259 |
260 | /**
261 | * The minimum order quantity of the buy box. -1 if not available, 0 if no limit exists.
262 | */
263 | public int buyBoxMinOrderQuantity = -1;
264 |
265 | /**
266 | * The maximum order quantity of the buy box. -1 if not available, 0 if no limit exists.
267 | */
268 | public int buyBoxMaxOrderQuantity = -1;
269 |
270 | /**
271 | * The availability message of the buy box. null if not available.
272 | * Example: “In Stock.”
273 | */
274 | public String buyBoxAvailabilityMessage = null;
275 |
276 | /**
277 | * The seller id of the buy box offer, if existent. Otherwise "-2" or null
278 | */
279 | public String buyBoxSellerId = null;
280 |
281 | /**
282 | * The default shipping country of the buy box seller. null if not available. Example: “US”
283 | */
284 | public String buyBoxShippingCountry = null;
285 |
286 | /**
287 | * If the buy box is Prime exclusive. null if not available.
288 | */
289 | public Boolean buyBoxIsPrimeExclusive = null;
290 |
291 | /**
292 | * If the buy box is free shipping eligible. null if not available.
293 | */
294 | public Boolean buyBoxIsFreeShippingEligible = null;
295 |
296 | /**
297 | * If the buy box is Prime eligible. null if not available.
298 | */
299 | public Boolean buyBoxIsPrimeEligible = null;
300 |
301 | /**
302 | * If the buy box is a Prime Pantry offer. null if not available.
303 | */
304 | public Boolean buyBoxIsPrimePantry = null;
305 |
306 | /**
307 | * A map containing buy box statistics for the interval specified. Each key represents the sellerId of the buy box seller and each object a buy box statistics object.
308 | */
309 | public LinkedHashMap buyBoxStats = null;
310 |
311 | /**
312 | * The buy box saving basis price (strikethrough, typical price). null if unavailable.
313 | */
314 | public Integer buyBoxSavingBasis = null;
315 |
316 | /**
317 | * Only set when the offers parameter was used. Price of the used buy box, if existent. Otherwise "-1" or null
318 | */
319 | public Integer buyBoxUsedPrice = null;
320 |
321 | /**
322 | * Only set when the offers parameter was used. Shipping cost of the used buy box, if existent. Otherwise "-1" or null
323 | */
324 | public Integer buyBoxUsedShipping = null;
325 |
326 | /**
327 | * Only set when the offers parameter was used. Seller id of the used boy box, if existent. Otherwise null.
328 | */
329 | public String buyBoxUsedSellerId = null;
330 |
331 | /**
332 | * Only set when the offers parameter was used. Whether or not the used buy box is fulfilled by Amazon.
333 | */
334 | public Boolean buyBoxUsedIsFBA = null;
335 |
336 | /**
337 | * Only set when the offers parameter was used. The used sub type condition of the used buy box offer
338 | *
The {@link Offer.OfferCondition} condition of the offered product. Integer value:
339 | *
2 - Used - Like New
340 | *
3 - Used - Very Good
341 | *
4 - Used - Good
342 | *
5 - Used - Acceptable
343 | *
Note: Open Box conditions will be coded as Used conditions.
344 | */
345 | public Byte buyBoxUsedCondition = null;
346 |
347 | /**
348 | * A map containing used buy box statistics for the interval specified. Each key represents the sellerId of the used buy box seller and each object a buy box statistics object.
349 | */
350 | public LinkedHashMap buyBoxUsedStats = null;
351 |
352 | /**
353 | * Only set when the offers parameter was used. If the product is an add-on item (add-on Items ship with orders that include $25 or more of items shipped by Amazon).
354 | */
355 | public Boolean isAddonItem = null;
356 |
357 | /**
358 | * Only set when the offers parameter was used. Contains the seller ids (if any) of the lowest priced live FBA offer(s). Multiple entries if multiple offers share the same price.
359 | */
360 | public String[] sellerIdsLowestFBA = null;
361 |
362 | /**
363 | * Only set when the offers parameter was used. Contains the seller ids (if any) of the lowest priced live FBM offer(s). Multiple entries if multiple offers share the same price.
364 | */
365 | public String[] sellerIdsLowestFBM = null;
366 |
367 | /**
368 | * Only set when the offers parameter was used. Count of retrieved live FBA offers.
369 | */
370 | public int offerCountFBA = -2;
371 |
372 | /**
373 | * Only set when the offers parameter was used. Count of retrieved live FBM offers.
374 | */
375 | public int offerCountFBM = -2;
376 |
377 | /**
378 | * The count of sales rank drops (from high value to low value) within the last 30 days which are considered to indicate sales.
379 | */
380 | public int salesRankDrops30 = -1;
381 |
382 | /**
383 | * The count of sales rank drops (from high value to low value) within the last 90 days which are considered to indicate sales.
384 | */
385 | public int salesRankDrops90 = -1;
386 |
387 | /**
388 | * The count of sales rank drops (from high value to low value) within the last 180 days which are considered to indicate sales.
389 | */
390 | public int salesRankDrops180 = -1;
391 |
392 | /**
393 | * The count of sales rank drops (from high value to low value) within the last 365 days which are considered to indicate sales.
394 | */
395 | public int salesRankDrops365 = -1;
396 |
397 | public static class BuyBoxStatsObject {
398 | /** an approximation of the percentage the seller won the buy box **/
399 | public float percentageWon;
400 | /** avg. price of the Buy Box offer of this seller **/
401 | public int avgPrice;
402 | /** avg. "New" offer count during the time the seller held the Buy Box **/
403 | public int avgNewOfferCount;
404 | /** whether or not this offer is fulfilled by Amazon **/
405 | public boolean isFBA;
406 | /** last time the seller won the buy box **/
407 | public int lastSeen;
408 | }
409 |
410 | @Override
411 | public String toString() {
412 | return gson.toJson(this);
413 | }
414 | }
415 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/structs/Request.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 | import com.keepa.api.backend.helper.KeepaTime;
4 |
5 | import java.util.HashMap;
6 |
7 | import static com.keepa.api.backend.helper.Utility.*;
8 |
9 | /**
10 | * Common Request
11 | */
12 | public class Request {
13 | // public enum RequestType {
14 | // REQUEST_PRODUCTS, BROWSING_DEALS, CATEGORY_LOOKUP, CATEGORY_SEARCH, PRODUCT_SEARCH, SELLER_REQUEST
15 | // }
16 |
17 | public HashMap parameter;
18 | public String postData;
19 | public String path;
20 |
21 | public Request() {
22 | parameter = new HashMap<>(20);
23 | }
24 |
25 | /**
26 | * By accessing our deals you can find products that recently changed and match your search criteria. A single request will return a maximum of 150 deals.
27 | *
28 | * @param dealRequest The dealRequest contains all request parameters.
29 | * @return A ready to send request.
30 | */
31 | public static Request getDealsRequest(DealRequest dealRequest) {
32 | Request r = new Request();
33 | r.path = "deal";
34 | r.postData = gson.toJson(dealRequest);
35 | return r;
36 | }
37 |
38 | /**
39 | * This request provides access to all current and upcoming lightning deals.
40 | * You can request a specific lightning deal by specifying an ASIN or get the complete list.
41 | * Our lightning deals information is updated every 10 minutes.
42 | *
43 | * @param domainId Amazon locale of the product {@link AmazonLocale}
44 | * @param asin The ASIN to retrieve the lightning deal for or null to retrieve all lightning deals
45 | * @return A ready to send request.
46 | */
47 | public static Request getLightningDealRequest(final AmazonLocale domainId, String asin) {
48 | Request r = new Request();
49 | r.path = "lightningdeal";
50 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
51 |
52 | if(asin != null)
53 | r.parameter.put("asin", asin);
54 | return r;
55 | }
56 |
57 | /**
58 | * Query our product database to find products matching your criteria. Almost all product fields can be searched for and sorted by.
59 | * The product finder request provides the same core functionality as our Product Finder.
60 | *
61 | * @param finderRequest The FinderRequest contains all filter parameters.
62 | * @return A ready to send request.
63 | */
64 | public static Request getProductFinderRequest(final AmazonLocale domainId, ProductFinderRequest finderRequest) {
65 | Request r = new Request();
66 | r.path = "query";
67 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
68 | r.parameter.put("selection", gson.toJson(finderRequest));
69 | return r;
70 | }
71 |
72 | /**
73 | * Add tracking to your list.
74 | *
75 | * @param trackingRequest The trackingRequest contains all request parameters.
76 | * @return A ready to send request.
77 | */
78 | public static Request getTrackingAddRequest(TrackingRequest trackingRequest) {
79 | return getTrackingBatchAddRequest(trackingRequest);
80 | }
81 |
82 | /**
83 | * Add multiple tracking to your list. Much more efficient than individual additions.
84 | *
85 | * @param trackingRequests The trackingRequests (up to 1000).
86 | * @return A ready to send request.
87 | */
88 | public static Request getTrackingBatchAddRequest(TrackingRequest... trackingRequests) {
89 | if(trackingRequests.length > 1000) return null;
90 | Request r = new Request();
91 | r.path = "tracking";
92 | r.parameter.put("type", "add");
93 | r.postData = gson.toJson(trackingRequests);
94 | return r;
95 | }
96 |
97 | /**
98 | * Get a single tracking from your list.
99 | *
100 | * @param asin The ASIN to retrieve the tracking for.
101 | * @return A ready to send request.
102 | */
103 | public static Request getTrackingGetRequest(String asin) {
104 | Request r = new Request();
105 | r.path = "tracking";
106 | r.parameter.put("type", "get");
107 | r.parameter.put("asin", asin);
108 | return r;
109 | }
110 |
111 | /**
112 | * Get all trackings from your list. If you track a lot of products this may be a resource intensive operation. Use responsibly.
113 | *
114 | * @param asinsOnly Wether or not to only request an ASIN list of tracked products or to return all tracking objects (much larger response).
115 | * @return A ready to send request.
116 | */
117 | public static Request getTrackingListRequest(boolean asinsOnly) {
118 | Request r = new Request();
119 | r.path = "tracking";
120 | r.parameter.put("type", "list");
121 | if(asinsOnly)
122 | r.parameter.put("asins-only", "1");
123 | return r;
124 | }
125 |
126 | /**
127 | * Get your recent notifications.
128 | * @param since Retrieve all available notifications that occurred since this date, in {@link KeepaTime}. Use 0 to request all.
129 | * @param revise Whether or not to request notifications already marked as read.
130 | * @return A ready to send request.
131 | */
132 | public static Request getTrackingNotificationRequest(int since, boolean revise) {
133 | Request r = new Request();
134 | r.path = "tracking";
135 | r.parameter.put("since", String.valueOf(since));
136 | r.parameter.put("revise", revise ? "1" : "0");
137 | r.parameter.put("type", "notification");
138 | return r;
139 | }
140 |
141 |
142 | /**
143 | * Remove a tracking from your list.
144 | *
145 | * @param asin The ASIN to remove the tracking for.
146 | * @return A ready to send request.
147 | */
148 | public static Request getTrackingRemoveRequest(String asin) {
149 | Request r = new Request();
150 | r.path = "tracking";
151 | r.parameter.put("type", "remove");
152 | r.parameter.put("asin", asin);
153 | return r;
154 | }
155 |
156 | /**
157 | * Removes all your trackings from your list.
158 | *
159 | * @return A ready to send request.
160 | */
161 | public static Request getTrackingRemoveAllRequest() {
162 | Request r = new Request();
163 | r.path = "tracking";
164 | r.parameter.put("type", "removeAll");
165 | return r;
166 | }
167 |
168 | /**
169 | * Updates the webhook URL our service will call whenever a notification is triggered. The URL can also be updated and tested via the website.
170 | * A push notification will be a HTTP POST call with a single notification object as its content.
171 | * Your server must respond with a status code of 200 to confirm the successful retrieval.
172 | * If delivery fails a second attempt will be made with a 15 seconds delay.
173 | *
174 | * @param url The new webhook URL
175 | * @return A ready to send request.
176 | */
177 | public static Request getTrackingWebhookRequest(String url) {
178 | Request r = new Request();
179 | r.path = "tracking";
180 | r.parameter.put("type", "webhook");
181 | r.parameter.put("url", url);
182 | return r;
183 | }
184 |
185 | /**
186 | * Retrieve an ASIN list of the most popular products based on sales in a specific category.
187 | *
188 | * @param domainId Amazon locale of the product {@link AmazonLocale}
189 | * @param category The category node id of the category you want to request the best sellers list for
190 | * @return A ready to send request.
191 | */
192 | public static Request getBestSellersRequest(final AmazonLocale domainId, long category) {
193 | return getBestSellersRequest(domainId, String.valueOf(category));
194 | }
195 |
196 | /**
197 | * Retrieve an ASIN list of the most popular products based on sales of a specific product group.
198 | *
199 | * @param domainId Amazon locale of the product {@link AmazonLocale}
200 | * @param productGroup The productGroup you want to request the best sellers list for
201 | * @return A ready to send request.
202 | */
203 | public static Request getBestSellersRequest(final AmazonLocale domainId, String productGroup) {
204 | Request r = new Request();
205 | r.path = "bestsellers";
206 | r.parameter.put("category", productGroup);
207 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
208 | return r;
209 | }
210 |
211 | /**
212 | * Retrieve category objects using their node ids and (optional) their parent tree.
213 | *
214 | * @param domainId Amazon locale of the product {@link AmazonLocale}
215 | * @param category The category node id of the category you want to request. For batch requests a comma separated list of ids (up to 10, the token cost stays the same).
216 | * @param parents Whether or not to include the category tree for each category.
217 | * @return A ready to send request.
218 | */
219 | public static Request getCategoryLookupRequest(final AmazonLocale domainId, boolean parents, long category) {
220 | Request r = new Request();
221 | r.path = "category";
222 | r.parameter.put("category", String.valueOf(category));
223 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
224 |
225 | if (parents)
226 | r.parameter.put("parents", "1");
227 |
228 | return r;
229 | }
230 |
231 | /**
232 | * Search for Amazon category names. Retrieves the category objects12 and optional their parent tree.
233 | *
234 | * @param domainId Amazon locale of the product {@link AmazonLocale}
235 | * @param term The term you want to search for. Multiple space separated keywords are possible and if provided must all match. The minimum length of a keyword is 3 characters.
236 | * @param parents Whether or not to include the category tree for each category.
237 | * @return A ready to send request.
238 | */
239 | public static Request getCategorySearchRequest(final AmazonLocale domainId, String term, boolean parents) {
240 | Request r = new Request();
241 | r.path = "search";
242 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
243 | r.parameter.put("type", "category");
244 | r.parameter.put("term", term);
245 |
246 | if (parents)
247 | r.parameter.put("parents", "1");
248 |
249 | return r;
250 | }
251 |
252 | /**
253 | * Retrieves the seller object via the seller id. If a seller is not found no tokens will be consumed.
254 | *
255 | * @param domainId Amazon locale of the product {@link AmazonLocale}
256 | * @param seller The seller id of the merchant you want to request. For batch requests a comma separated list of sellerIds (up to 100). The seller id is part of the offer object and can also be found on Amazon on seller profile pages in the seller parameter of the URL.
257 | * Example: A2L77EE7U53NWQ (Amazon.com Warehouse Deals)
258 | * @return A ready to send request.
259 | */
260 | public static Request getSellerRequest(final AmazonLocale domainId, String... seller) {
261 | Request r = new Request();
262 | r.path = "seller";
263 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
264 | r.parameter.put("seller", arrayToCsv(seller));
265 | return r;
266 | }
267 |
268 | /**
269 | * Retrieves the seller object via the seller id. If a seller is not found no tokens will be consumed.
270 | *
271 | * @param domainId Amazon locale of the product {@link AmazonLocale}
272 | * @param seller The seller id of the merchant you want to request. For batch requests a comma separated list of sellerIds (up to 100).
273 | * The seller id is part of the offer object and can also be found on Amazon on seller profile pages in the seller parameter of the URL.
274 | * Example: A2L77EE7U53NWQ (Amazon.com Warehouse Deals)
275 | * @param storefront Valid values: 0 (false) and 1 (true). If specified the seller object will contain additional information about what items the seller is listing on Amazon.
276 | * This includes a list of ASINs as well as the total amount of items the seller has listed.
277 | * The following seller object fields will be set if data is available: asinList, asinListLastSeen.
278 | * If no data is available no additional tokens will be consumed. The ASIN list can contain up to 100,000 items.
279 | * As using the storefront parameter does not trigger any new collection it does not increase the processing time
280 | * of the request, though the response may be much bigger in size.
281 | * @return A ready to send request.
282 | */
283 | public static Request getSellerRequest(final AmazonLocale domainId, String seller, boolean storefront) {
284 | Request r = new Request();
285 | r.path = "seller";
286 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
287 | r.parameter.put("seller", seller);
288 |
289 | if (storefront)
290 | r.parameter.put("storefront", "1");
291 |
292 | return r;
293 | }
294 |
295 | /**
296 | * Retrieves the seller object via the seller id. If a seller is not found no tokens will be consumed.
297 | *
298 | * @param domainId Amazon locale of the product {@link AmazonLocale}
299 | * @param seller The seller id of the merchant you want to request. For batch requests a comma separated list of sellerIds (up to 100).
300 | * The seller id is part of the offer object and can also be found on Amazon on seller profile pages in the seller parameter of the URL.
301 | * Example: A2L77EE7U53NWQ (Amazon.com Warehouse Deals)
302 | * @param storefront Valid values: 0 (false) and 1 (true). If specified the seller object will contain additional information about what items the seller is listing on Amazon.
303 | * This includes a list of ASINs as well as the total amount of items the seller has listed.
304 | * The following seller object fields will be set if data is available: asinList, asinListLastSeen.
305 | * If no data is available no additional tokens will be consumed. The ASIN list can contain up to 100,000 items.
306 | * As using the storefront parameter does not trigger any new collection it does not increase the processing time
307 | * of the request, though the response may be much bigger in size.
308 | * @param update Positive integer value. If the last live data collection from the Amazon storefront page is older than update hours force a new collection. Use this parameter in conjunction with the
309 | * storefront parameter. Token cost will only be applied if a new collection is triggered.
310 | * @return A ready to send request.
311 | */
312 | public static Request getSellerRequest(final AmazonLocale domainId, String seller, boolean storefront, int update) {
313 | Request r = new Request();
314 | r.path = "seller";
315 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
316 | r.parameter.put("seller", seller);
317 | if (update >= 0)
318 | r.parameter.put("update", String.valueOf(update));
319 |
320 | if (storefront || update >= 0)
321 | r.parameter.put("storefront", "1");
322 |
323 | return r;
324 | }
325 |
326 | /**
327 | * Retrieve a Seller ID list of the most rated Amazon marketplace sellers.
328 | *
329 | * @param domainId Amazon locale of the product {@link AmazonLocale}. China is not supported.
330 | * @return A ready to send request.
331 | */
332 | public static Request getTopSellerRequest(final AmazonLocale domainId) {
333 | Request r = new Request();
334 | r.path = "topseller";
335 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
336 | return r;
337 | }
338 |
339 | /**
340 | * Search for Amazon products using keywords with a maximum of 50 results per search term.
341 | *
342 | * @param domainId Amazon locale of the product {@link AmazonLocale}
343 | * @param term The term you want to search for.
344 | * @param stats If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values of the last x days, where x is the value of the stats parameter.
345 | * @return A ready to send request.
346 | */
347 | public static Request getProductSearchRequest(final AmazonLocale domainId, String term, Integer stats) {
348 | Request r = new Request();
349 | r.path = "search";
350 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
351 | r.parameter.put("type", "product");
352 | r.parameter.put("term", term);
353 |
354 | if (stats != null && stats > 0)
355 | r.parameter.put("stats", String.valueOf(stats));
356 |
357 | return r;
358 | }
359 |
360 |
361 | /**
362 | * Search for Amazon products using keywords with a maximum of 50 results per search term.
363 | *
364 | * @param domainId Amazon locale of the product {@link AmazonLocale}
365 | * @param term The term you want to search for.
366 | * @param stats If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values of the last x days, where x is the value of the stats parameter.
367 | * @param page Valid values 0 - 9. Each search result page provides up to 10 results. To retrieve more results iterate the page parameter and keep all other parameters identical. Start with page 0 and stop when the response contains less than 10 results or you have reached page 9, which is the limit. When not using the page parameter the first 40 results will be returned.
368 | * @return A ready to send request.
369 | */
370 | public static Request getProductSearchRequest(final AmazonLocale domainId, String term, Integer stats, int page) {
371 | Request r = new Request();
372 | r.path = "search";
373 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
374 | r.parameter.put("type", "product");
375 | r.parameter.put("term", term);
376 | r.parameter.put("page", String.valueOf(page));
377 |
378 | if (stats != null && stats > 0)
379 | r.parameter.put("stats", String.valueOf(stats));
380 |
381 | return r;
382 | }
383 |
384 | /**
385 | * Search for Amazon products using keywords with a maximum of 50 results per search term.
386 | *
387 | * @param domainId Amazon locale of the product {@link AmazonLocale}
388 | * @param term The term you want to search for.
389 | * @param history Whether or not to include the product's history data (csv field). If you do not evaluate the csv field set to false to speed up the request and reduce traffic.
390 | * @param update If the product's last refresh is older than update-hours force a refresh. Use this to speed up requests if up-to-date data is not required. Might cost an extra token if 0 (= live data). Default 1.
391 | * @param stats If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values of the last x days, where x is the value of the stats parameter.
392 | * @param asinsOnly If true only the ASINs of the found products will be provided (instead of the product objects).
393 | * @return A ready to send request.
394 | */
395 | public static Request getProductSearchRequest(final AmazonLocale domainId, String term, Integer stats, int update, boolean history, boolean asinsOnly) {
396 | Request r = new Request();
397 | r.path = "search";
398 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
399 | r.parameter.put("type", "product");
400 | r.parameter.put("term", term);
401 | r.parameter.put("update", String.valueOf(update));
402 | r.parameter.put("history", history ? "1" : "0");
403 | r.parameter.put("asins-only", asinsOnly ? "1" : "0");
404 |
405 | if (stats != null && stats > 0)
406 | r.parameter.put("stats", String.valueOf(stats));
407 |
408 | return r;
409 | }
410 |
411 | /**
412 | * Search for Amazon products using keywords with a maximum of 50 results per search term.
413 | *
414 | * @param domainId Amazon locale of the product {@link AmazonLocale}
415 | * @param term The term you want to search for.
416 | * @param history Whether or not to include the product's history data (csv field). If you do not evaluate the csv field set to false to speed up the request and reduce traffic.
417 | * @param update If the product's last refresh is older than update-hours force a refresh. Use this to speed up requests if up-to-date data is not required. Might cost an extra token if 0 (= live data). Default 1.
418 | * @param stats If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values of the last x days, where x is the value of the stats parameter.
419 | * @param asinsOnly If true only the ASINs of the found products will be provided (instead of the product objects).
420 | * @param page Valid values 0 - 9. Each search result page provides up to 10 results. To retrieve more results iterate the page parameter and keep all other parameters identical. Start with page 0 and stop when the response contains less than 10 results or you have reached page 9, which is the limit. When not using the page parameter the first 40 results will be returned.
421 | * @return A ready to send request.
422 | */
423 | public static Request getProductSearchRequest(final AmazonLocale domainId, String term, Integer stats, int update, boolean history, boolean asinsOnly, int page) {
424 | Request r = new Request();
425 | r.path = "search";
426 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
427 | r.parameter.put("type", "product");
428 | r.parameter.put("term", term);
429 | r.parameter.put("update", String.valueOf(update));
430 | r.parameter.put("history", history ? "1" : "0");
431 | r.parameter.put("asins-only", asinsOnly ? "1" : "0");
432 | r.parameter.put("page", String.valueOf(page));
433 |
434 | if (stats != null && stats > 0)
435 | r.parameter.put("stats", String.valueOf(stats));
436 |
437 | return r;
438 | }
439 |
440 | /**
441 | * Retrieves the product object for the specified ASIN and domain.
442 | *
443 | * @param domainId Amazon locale of the product {@link AmazonLocale}
444 | * @param asins ASINs to request, must contain between 1 and 100 ASINs - or max 20 ASINs if the offers parameter is used.
445 | * @param stats If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values of the last x days, where x is the value of the stats parameter.
446 | * @param offers If specified (= not null) determines the number of marketplace offers to retrieve.
447 | * @return A ready to send request.
448 | */
449 | public static Request getProductRequest(final AmazonLocale domainId, Integer stats, Integer offers, final String... asins) {
450 | Request r = new Request();
451 | r.path = "product";
452 | r.parameter.put("asin", arrayToCsv(asins));
453 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
454 | if (stats != null && stats > 0)
455 | r.parameter.put("stats", String.valueOf(stats));
456 |
457 | if (offers != null && offers > 0)
458 | r.parameter.put("offers", String.valueOf(offers));
459 |
460 | return r;
461 | }
462 |
463 | /**
464 | * Retrieves the product object(s) for the specified product code and domain.
465 | *
466 | * @param domainId Amazon locale of the product {@link AmazonLocale}
467 | * @param codes The product code of the product you want to request. We currently allow UPC, EAN and ISBN-13 codes. For batch requests a comma separated list of codes (up to 100). Multiple ASINs can have the same product code, so requesting a product code can return multiple products.
468 | * @param stats If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values of the last x days, where x is the value of the stats parameter.
469 | * @param offers If specified (= not null) determines the number of marketplace offers to retrieve.
470 | * @return A ready to send request.
471 | */
472 | public static Request getProductByCodeRequest(final AmazonLocale domainId, Integer stats, Integer offers, final String... codes) {
473 | Request r = new Request();
474 | r.path = "product";
475 | r.parameter.put("code", arrayToCsv(codes));
476 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
477 | if (stats != null && stats > 0)
478 | r.parameter.put("stats", String.valueOf(stats));
479 |
480 | if (offers != null && offers > 0)
481 | r.parameter.put("offers", String.valueOf(offers));
482 |
483 | return r;
484 | }
485 |
486 | /**
487 | * Retrieves the product object for the specified ASIN and domain.
488 | *
489 | * @param domainId Amazon locale of the product {@link AmazonLocale}
490 | * @param asins ASINs to request, must contain between 1 and 100 ASINs - or max 20 ASINs if the offers parameter is used.
491 | * @param stats If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values of the last x days, where x is the value of the stats parameter.
492 | * @param history Whether or not to include the product's history data (csv field). If you do not evaluate the csv field set to false to speed up the request and reduce traffic.
493 | * @param update If the product's last refresh is older than update-hours force a refresh. Use this to speed up requests if up-to-date data is not required. Might cost an extra token if 0 (= live data). Default 1.
494 | * @param offers If specified (= not null) determines the number of marketplace offers to retrieve.
495 | * @return A ready to send request.
496 | */
497 | public static Request getProductRequest(final AmazonLocale domainId, Integer stats, Integer offers, int update, boolean history, final String... asins) {
498 | if (stats == null) {
499 | return getProductRequest(domainId, offers, null, null, update, history, asins);
500 | } else {
501 | long now = System.currentTimeMillis();
502 | return getProductRequest(domainId, offers, String.valueOf((now - (stats * 24 * 60 * 60 * 1000L))), String.valueOf(now), update, history, asins);
503 | }
504 | }
505 |
506 | /**
507 | * Retrieves the product object(s) for the specified product code and domain.
508 | *
509 | * @param domainId Amazon locale of the product {@link AmazonLocale}
510 | * @param codes The product code of the product you want to request. We currently allow UPC, EAN and ISBN-13 codes. For batch requests a comma separated list of codes (up to 100). Multiple ASINs can have the same product code, so requesting a product code can return multiple products.
511 | * @param stats If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values of the last x days, where x is the value of the stats parameter.
512 | * @param history Whether or not to include the product's history data (csv field). If you do not evaluate the csv field set to false to speed up the request and reduce traffic.
513 | * @param update If the product's last refresh is older than update-hours force a refresh. Use this to speed up requests if up-to-date data is not required. Might cost an extra token if 0 (= live data). Default 1.
514 | * @param offers If specified (= not null) determines the number of marketplace offers to retrieve.
515 | * @return A ready to send request.
516 | */
517 | public static Request getProductByCodeRequest(final AmazonLocale domainId, Integer stats, Integer offers, int update, boolean history, final String... codes) {
518 | if (stats == null) {
519 | return getProductByCodeRequest(domainId, offers, null, null, update, history, codes);
520 | } else {
521 | long now = System.currentTimeMillis();
522 | return getProductByCodeRequest(domainId, offers, String.valueOf((now - (stats * 24 * 60 * 60 * 1000L))), String.valueOf(now), update, history, codes);
523 | }
524 | }
525 |
526 | /**
527 | * Retrieves the product object for the specified ASIN and domain.
528 | *
529 | * @param domainId Amazon locale of the product {@link AmazonLocale}
530 | * @param asins ASINs to request, must contain between 1 and 100 ASINs - or max 20 ASINs if the offers parameter is used.
531 | * @param statsStartDate Must be Unix epoch time milliseconds. If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values in the interval specified statsStartDate to statsEndDate. .
532 | * @param statsEndDate the end of the stats interval. See statsStartDate.
533 | * @param history Whether or not to include the product's history data (csv field). If you do not evaluate the csv field set to false to speed up the request and reduce traffic.
534 | * @param update If the product's last refresh is older than update-hours force a refresh. Use this to speed up requests if up-to-date data is not required. Might cost an extra token if 0 (= live data). Default 1.
535 | * @param offers If specified (= not null) determines the number of marketplace offers to retrieve.
536 | * @return A ready to send request.
537 | */
538 | public static Request getProductRequest(final AmazonLocale domainId, Long statsStartDate, Long statsEndDate, Integer offers, int update, boolean history, final String... asins) {
539 | if (statsStartDate == null) {
540 | return getProductRequest(domainId, offers, null, null, update, history, asins);
541 | } else {
542 | return getProductRequest(domainId, offers, String.valueOf(statsStartDate), String.valueOf(statsEndDate), update, history, asins);
543 | }
544 | }
545 |
546 |
547 | /**
548 | * Retrieves the product object(s) for the specified product code and domain.
549 | *
550 | * @param domainId Amazon locale of the product {@link AmazonLocale}
551 | * @param codes The product code of the product you want to request. We currently allow UPC, EAN and ISBN-13 codes. For batch requests a comma separated list of codes (up to 100). Multiple ASINs can have the same product code, so requesting a product code can return multiple products.
552 | * @param statsStartDate Must be Unix epoch time milliseconds. If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values in the interval specified statsStartDate to statsEndDate. .
553 | * @param statsEndDate the end of the stats interval. See statsStartDate.
554 | * @param history Whether or not to include the product's history data (csv field). If you do not evaluate the csv field set to false to speed up the request and reduce traffic.
555 | * @param update If the product's last refresh is older than update-hours force a refresh. Use this to speed up requests if up-to-date data is not required. Might cost an extra token if 0 (= live data). Default 1.
556 | * @param offers If specified (= not null) determines the number of marketplace offers to retrieve.
557 | * @return A ready to send request.
558 | */
559 | public static Request getProductByCodeRequest(final AmazonLocale domainId, Long statsStartDate, Long statsEndDate, Integer offers, int update, boolean history, final String... codes) {
560 | if (statsStartDate == null) {
561 | return getProductByCodeRequest(domainId, offers, null, null, update, history, codes);
562 | } else {
563 | return getProductByCodeRequest(domainId, offers, String.valueOf(statsStartDate), String.valueOf(statsEndDate), update, history, codes);
564 | }
565 | }
566 |
567 | /**
568 | * Retrieves the product object for the specified ASIN and domain.
569 | *
570 | * @param domainId Amazon locale of the product {@link AmazonLocale}
571 | * @param asins ASINs to request, must contain between 1 and 100 ASINs - or max 20 ASINs if the offers parameter is used.
572 | * @param statsStartDate Must ISO8601 coded date (with or without time in UTC). Example: 2015-12-31 or 2015-12-31T14:51Z. If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values in the interval specified statsStartDate to statsEndDate. .
573 | * @param statsEndDate the end of the stats interval. See statsStartDate.
574 | * @param history Whether or not to include the product's history data (csv field). If you do not evaluate the csv field set to false to speed up the request and reduce traffic.
575 | * @param update If the product's last refresh is older than update-hours force a refresh. Use this to speed up requests if up-to-date data is not required. Might cost an extra token if 0 (= live data). Default 1.
576 | * @param offers If specified (= not null) determines the number of marketplace offers to retrieve.
577 | * @return A ready to send request.
578 | */
579 | public static Request getProductRequest(final AmazonLocale domainId, Integer offers, String statsStartDate, String statsEndDate, int update, boolean history, final String... asins) {
580 | Request r = new Request();
581 | r.path = "product";
582 | r.parameter.put("asin", arrayToCsv(asins));
583 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
584 | r.parameter.put("update", String.valueOf(update));
585 | r.parameter.put("history", history ? "1" : "0");
586 |
587 | if (statsStartDate != null && statsEndDate != null)
588 | r.parameter.put("stats", statsStartDate + "," + statsEndDate);
589 |
590 | if (offers != null && offers > 0)
591 | r.parameter.put("offers", String.valueOf(offers));
592 |
593 | return r;
594 | }
595 |
596 | /**
597 | * Retrieves the product object for the specified ASIN and domain.
598 | *
599 | * @param domainId Amazon locale of the product {@link AmazonLocale}
600 | * @param asins ASINs to request, must contain between 1 and 100 ASINs - or max 20 ASINs if the offers parameter is used.
601 | * @param statsStartDate Must ISO8601 coded date (with or without time in UTC). Example: 2015-12-31 or 2015-12-31T14:51Z. If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values in the interval specified statsStartDate to statsEndDate. .
602 | * @param statsEndDate the end of the stats interval. See statsStartDate.
603 | * @param history Whether or not to include the product's history data (csv field). If you do not evaluate the csv field set to false to speed up the request and reduce traffic.
604 | * @param buybox If specified and true the product and statistics object will include all available buy box related data
605 | * @param update If the product's last refresh is older than update-hours force a refresh. Use this to speed up requests if up-to-date data is not required. Might cost an extra token if 0 (= live data). Default 1.
606 | * @param offers If specified (= not null) determines the number of marketplace offers to retrieve.
607 | * @param rental If true the rental price will be collected when available. Can only be used in conjunction with the offers parameter.
608 | * @param rating If true the product object will include our existing RATING and COUNT_REVIEWS history of the csv field, regardless if the offers parameter is used
609 | * @return A ready to send request.
610 | */
611 | public static Request getProductRequest(final AmazonLocale domainId, Integer offers, String statsStartDate, String statsEndDate, boolean buybox, int update, boolean history,
612 | boolean rental, boolean rating, final String... asins) {
613 | Request r = new Request();
614 | r.path = "product";
615 | r.parameter.put("asin", arrayToCsv(asins));
616 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
617 | r.parameter.put("update", String.valueOf(update));
618 | r.parameter.put("history", history ? "1" : "0");
619 | r.parameter.put("rental", rental ? "1" : "0");
620 | r.parameter.put("rating", rating ? "1" : "0");
621 | r.parameter.put("buybox", buybox ? "1" : "0");
622 |
623 | if (statsStartDate != null && statsEndDate != null)
624 | r.parameter.put("stats", statsStartDate + "," + statsEndDate);
625 |
626 | if (offers != null && offers > 0)
627 | r.parameter.put("offers", String.valueOf(offers));
628 |
629 | return r;
630 | }
631 |
632 |
633 | /**
634 | * Retrieves the product object for the specified ASIN and domain.
635 | *
636 | * @param domainId Amazon locale of the product {@link AmazonLocale}
637 | * @param asins ASINs to request, must contain between 1 and 100 ASINs - or max 20 ASINs if the offers parameter is used.
638 | * @param statsStartDate Must ISO8601 coded date (with or without time in UTC). Example: 2015-12-31 or 2015-12-31T14:51Z. If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values in the interval specified statsStartDate to statsEndDate. .
639 | * @param statsEndDate the end of the stats interval. See statsStartDate.
640 | * @param history Whether or not to include the product's history data (csv field). If you do not evaluate the csv field set to false to speed up the request and reduce traffic.
641 | * @param buybox If specified and true the product and statistics object will include all available buy box related data
642 | * @param update If the product's last refresh is older than update-hours force a refresh. Use this to speed up requests if up-to-date data is not required. Might cost an extra token if 0 (= live data). Default 1.
643 | * @param offers If specified (= not null) determines the number of marketplace offers to retrieve.
644 | * @param rental If true the rental price will be collected when available. Can only be used in conjunction with the offers parameter.
645 | * @param rating If true the product object will include our existing RATING and COUNT_REVIEWS history of the csv field, regardless if the offers parameter is used
646 | * @param onlyLiveOffers If specified and has the value 1 the product object will only include live marketplace offers (when used in combination with the offers parameter). If you do not need historical offers use this to have them removed from the response. This can improve processing time and considerably decrease the size of the response.
647 | * @param days Any positive integer value. If specified and has positive value X the product object will limit all historical data to the recent X days. This includes the csv, buyBoxSellerIdHistory, salesRanks, offers and offers.offerCSV fields. If you do not need old historical data use this to have it removed from the response. This can improve processing time and considerably decrease the size of the response.
648 | * @return A ready to send request.
649 | */
650 | public static Request getProductRequest(final AmazonLocale domainId, Integer offers, String statsStartDate, String statsEndDate, boolean buybox, int update, boolean history,
651 | boolean rental, boolean rating, boolean onlyLiveOffers, int days, final String... asins) {
652 | Request r = new Request();
653 | r.path = "product";
654 | r.parameter.put("asin", arrayToCsv(asins));
655 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
656 | r.parameter.put("update", String.valueOf(update));
657 | r.parameter.put("history", history ? "1" : "0");
658 | r.parameter.put("rental", rental ? "1" : "0");
659 | r.parameter.put("rating", rating ? "1" : "0");
660 | r.parameter.put("buybox", buybox ? "1" : "0");
661 | r.parameter.put("only-live-offers", onlyLiveOffers ? "1" : "0");
662 | r.parameter.put("days", String.valueOf(days));
663 |
664 | if (statsStartDate != null && statsEndDate != null)
665 | r.parameter.put("stats", statsStartDate + "," + statsEndDate);
666 |
667 | if (offers != null && offers > 0)
668 | r.parameter.put("offers", String.valueOf(offers));
669 |
670 | return r;
671 | }
672 |
673 | /**
674 | * Retrieves the product object for the specified ASIN and domain.
675 | *
676 | * @param domainId Amazon locale of the product {@link AmazonLocale}
677 | * @param asins ASINs to request, must contain between 1 and 100 ASINs - or max 20 ASINs if the offers parameter is used.
678 | * @param statsStartDate Must ISO8601 coded date (with or without time in UTC). Example: 2015-12-31 or 2015-12-31T14:51Z. If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values in the interval specified statsStartDate to statsEndDate. .
679 | * @param statsEndDate the end of the stats interval. See statsStartDate.
680 | * @param history Whether or not to include the product's history data (csv field). If you do not evaluate the csv field set to false to speed up the request and reduce traffic.
681 | * @param buybox If specified and true the product and statistics object will include all available buy box related data
682 | * @param update If the product's last refresh is older than update-hours force a refresh. Use this to speed up requests if up-to-date data is not required. Might cost an extra token if 0 (= live data). Default 1.
683 | * @param offers If specified (= not null) determines the number of marketplace offers to retrieve.
684 | * @param rental If true the rental price will be collected when available. Can only be used in conjunction with the offers parameter.
685 | * @param rating If true the product object will include our existing RATING and COUNT_REVIEWS history of the csv field, regardless if the offers parameter is used
686 | * @param videos If true the product object will include video metadata
687 | * @param aplus If true the product object will include A+ content
688 | * @param stock If true the product object will include stock data
689 | * @param onlyLiveOffers If specified and has the value 1 the product object will only include live marketplace offers (when used in combination with the offers parameter). If you do not need historical offers use this to have them removed from the response. This can improve processing time and considerably decrease the size of the response.
690 | * @param days Any positive integer value. If specified and has positive value X the product object will limit all historical data to the recent X days. This includes the csv, buyBoxSellerIdHistory, salesRanks, offers and offers.offerCSV fields. If you do not need old historical data use this to have it removed from the response. This can improve processing time and considerably decrease the size of the response.
691 | * @return A ready to send request.
692 | */
693 | public static Request getProductRequest(final AmazonLocale domainId, Integer offers, String statsStartDate, String statsEndDate, boolean buybox, int update, boolean history,
694 | boolean rental, boolean rating, boolean videos, boolean aplus, boolean stock, boolean onlyLiveOffers, int days, final String... asins) {
695 | Request r = new Request();
696 | r.path = "product";
697 | r.parameter.put("asin", arrayToCsv(asins));
698 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
699 | r.parameter.put("update", String.valueOf(update));
700 | r.parameter.put("history", history ? "1" : "0");
701 | r.parameter.put("rental", rental ? "1" : "0");
702 | r.parameter.put("rating", rating ? "1" : "0");
703 | r.parameter.put("buybox", buybox ? "1" : "0");
704 | r.parameter.put("videos", videos ? "1" : "0");
705 | r.parameter.put("aplus", aplus ? "1" : "0");
706 | r.parameter.put("stock", stock ? "1" : "0");
707 | r.parameter.put("only-live-offers", onlyLiveOffers ? "1" : "0");
708 | r.parameter.put("days", String.valueOf(days));
709 |
710 | if (statsStartDate != null && statsEndDate != null)
711 | r.parameter.put("stats", statsStartDate + "," + statsEndDate);
712 |
713 | if (offers != null && offers > 0)
714 | r.parameter.put("offers", String.valueOf(offers));
715 |
716 | return r;
717 | }
718 |
719 |
720 | /**
721 | * Retrieves the product object(s) for the specified product code and domain.
722 | *
723 | * @param domainId Amazon locale of the product {@link AmazonLocale}
724 | * @param codes The product code of the product you want to request. We currently allow UPC, EAN and ISBN-13 codes. For batch requests a comma separated list of codes (up to 100). Multiple ASINs can have the same product code, so requesting a product code can return multiple products.
725 | * @param statsStartDate Must ISO8601 coded date (with or without time in UTC). Example: 2015-12-31 or 2015-12-31T14:51Z. If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values in the interval specified statsStartDate to statsEndDate. .
726 | * @param statsEndDate the end of the stats interval. See statsStartDate.
727 | * @param history Whether or not to include the product's history data (csv field). If you do not evaluate the csv field set to false to speed up the request and reduce traffic.
728 | * @param update If the product's last refresh is older than update-hours force a refresh. Use this to speed up requests if up-to-date data is not required. Might cost an extra token if 0 (= live data). Default 1.
729 | * @param offers If specified (= not null) determines the number of marketplace offers to retrieve.
730 | * @return A ready to send request.
731 | */
732 | public static Request getProductByCodeRequest(final AmazonLocale domainId, Integer offers, String statsStartDate, String statsEndDate, int update, boolean history, final String... codes) {
733 | Request r = new Request();
734 | r.path = "product";
735 | r.parameter.put("code", arrayToCsv(codes));
736 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
737 | r.parameter.put("update", String.valueOf(update));
738 | r.parameter.put("history", history ? "1" : "0");
739 |
740 | if (statsStartDate != null && statsEndDate != null)
741 | r.parameter.put("stats", statsStartDate + "," + statsEndDate);
742 |
743 | if (offers != null && offers > 0)
744 | r.parameter.put("offers", String.valueOf(offers));
745 |
746 | return r;
747 | }
748 |
749 | /**
750 | * Retrieves the product object(s) for the specified product code and domain.
751 | *
752 | * @param domainId Amazon locale of the product {@link AmazonLocale}
753 | * @param codes The product code of the product you want to request. We currently allow UPC, EAN and ISBN-13 codes. For batch requests a comma separated list of codes (up to 100). Multiple ASINs can have the same product code, so requesting a product code can return multiple products.
754 | * @param statsStartDate Must ISO8601 coded date (with or without time in UTC). Example: 2015-12-31 or 2015-12-31T14:51Z. If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values in the interval specified statsStartDate to statsEndDate. .
755 | * @param statsEndDate the end of the stats interval. See statsStartDate.
756 | * @param history Whether or not to include the product's history data (csv field). If you do not evaluate the csv field set to false to speed up the request and reduce traffic.
757 | * @param update If the product's last refresh is older than update-hours force a refresh. Use this to speed up requests if up-to-date data is not required. Might cost an extra token if 0 (= live data). Default 1.
758 | * @param offers If specified (= not null) determines the number of marketplace offers to retrieve.
759 | * @param rental If true the rental price will be collected when available. Can only be used in conjunction with the offers parameter.
760 | * @param rating If true the product object will include our existing RATING and COUNT_REVIEWS history of the csv field, regardless if the offers parameter is used
761 | * @return A ready to send request.
762 | */
763 | public static Request getProductByCodeRequest(final AmazonLocale domainId, Integer offers, String statsStartDate, String statsEndDate, int update, boolean history,
764 | boolean rental, boolean rating, final String... codes) {
765 | Request r = new Request();
766 | r.path = "product";
767 | r.parameter.put("code", arrayToCsv(codes));
768 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
769 | r.parameter.put("update", String.valueOf(update));
770 | r.parameter.put("history", history ? "1" : "0");
771 | r.parameter.put("rental", rental ? "1" : "0");
772 | r.parameter.put("rating", rating ? "1" : "0");
773 |
774 | if (statsStartDate != null && statsEndDate != null)
775 | r.parameter.put("stats", statsStartDate + "," + statsEndDate);
776 |
777 | if (offers != null && offers > 0)
778 | r.parameter.put("offers", String.valueOf(offers));
779 |
780 | return r;
781 | }
782 |
783 | /**
784 | * Retrieves the product object(s) for the specified product code and domain.
785 | *
786 | * @param domainId Amazon locale of the product {@link AmazonLocale}
787 | * @param codes The product code of the product you want to request. We currently allow UPC, EAN and ISBN-13 codes. For batch requests a comma separated list of codes (up to 100). Multiple ASINs can have the same product code, so requesting a product code can return multiple products.
788 | * @param statsStartDate Must ISO8601 coded date (with or without time in UTC). Example: 2015-12-31 or 2015-12-31T14:51Z. If specified (= not null) the product object will have a stats field with quick access to current prices, min/max prices and the weighted mean values in the interval specified statsStartDate to statsEndDate. .
789 | * @param statsEndDate the end of the stats interval. See statsStartDate.
790 | * @param history Whether or not to include the product's history data (csv field). If you do not evaluate the csv field set to false to speed up the request and reduce traffic.
791 | * @param buybox If specified and true the product and statistics object will include all available buy box related data
792 | * @param update If the product's last refresh is older than update-hours force a refresh. Use this to speed up requests if up-to-date data is not required. Might cost an extra token if 0 (= live data). Default 1.
793 | * @param offers If specified (= not null) determines the number of marketplace offers to retrieve.
794 | * @param rental If true the rental price will be collected when available. Can only be used in conjunction with the offers parameter.
795 | * @param rating If true the product object will include our existing RATING and COUNT_REVIEWS history of the csv field, regardless if the offers parameter is used
796 | * @return A ready to send request.
797 | */
798 | public static Request getProductByCodeRequest(final AmazonLocale domainId, Integer offers, String statsStartDate, String statsEndDate, boolean buybox, int update, boolean history,
799 | boolean rental, boolean rating, final String... codes) {
800 | Request r = new Request();
801 | r.path = "product";
802 | r.parameter.put("code", arrayToCsv(codes));
803 | r.parameter.put("domain", String.valueOf(domainId.ordinal()));
804 | r.parameter.put("update", String.valueOf(update));
805 | r.parameter.put("history", history ? "1" : "0");
806 | r.parameter.put("rental", rental ? "1" : "0");
807 | r.parameter.put("rating", rating ? "1" : "0");
808 | r.parameter.put("buybox", buybox ? "1" : "0");
809 |
810 | if (statsStartDate != null && statsEndDate != null)
811 | r.parameter.put("stats", statsStartDate + "," + statsEndDate);
812 |
813 | if (offers != null && offers > 0)
814 | r.parameter.put("offers", String.valueOf(offers));
815 |
816 | return r;
817 | }
818 |
819 |
820 | @Override
821 | public String toString() {
822 | return gsonPretty.toJson(this);
823 | }
824 | }
--------------------------------------------------------------------------------