├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
└── main
└── java
└── com
└── keepa
└── api
└── backend
├── KeepaAPI.java
├── exceptions
└── KeepaAPIException.java
├── helper
├── BasicNameFactory.java
├── KeepaTime.java
├── ProductAnalyzer.java
└── Utility.java
└── structs
├── AmazonLocale.java
├── BestSellers.java
├── Category.java
├── Deal.java
├── DealRequest.java
├── DealResponse.java
├── LightningDeal.java
├── Notification.java
├── Offer.java
├── Product.java
├── ProductFinderRequest.java
├── Request.java
├── RequestError.java
├── Response.java
├── Seller.java
├── Stats.java
├── Tracking.java
└── TrackingRequest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 |
3 |
4 | # Mobile Tools for Java (J2ME)
5 | .mtj.tmp/
6 |
7 | # Package Files #
8 | *.jar
9 | *.war
10 | *.ear
11 | *.txt
12 |
13 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
14 | hs_err_pid*
15 |
16 | target/*
17 | *.iml
18 |
19 | out/*
20 | .idea
21 | composer.json
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
16 |
17 | Keepa API Framework
18 | ==============================
19 |
20 | This framework is intended for users of the Keepa API.
21 |
22 | Features
23 | --------
24 | * Retrieves content from the API asynchronously and in parallel via Deferred callbacks (Java 8 Lambda friendly)
25 | * Parses API response to easy to use Java objects
26 | * Provides methods that facilitate the work with price history data
27 |
28 | Maven
29 | -----
30 | ```xml
31 |
32 |
33 | Keepa
34 | Keepa Repository
35 | https://keepa.com/maven/
36 |
37 | ...
38 |
39 |
40 |
41 |
42 | com.keepa.api
43 | backend
44 | LATEST
45 |
46 | ...
47 |
48 | ```
49 |
50 | Gradle
51 | -----
52 | ```xml
53 | repositories {
54 | ...
55 | maven { url 'https://keepa.com/maven/' }
56 | }
57 | dependencies {
58 | ...
59 | compile 'com.keepa.api:backend:latest.release'
60 | }
61 |
62 | Also consider to add
63 |
64 | configurations.all {
65 | resolutionStrategy {
66 | cacheDynamicVersionsFor 0, 'seconds'
67 | cacheChangingModulesFor 0, 'seconds'
68 | }
69 | }
70 |
71 | Which makes sure that the newest version from our servers is pulled during build.
72 | ```
73 |
74 |
75 | Quick Example
76 | ==============
77 |
78 | Make an API request
79 | ---------------------------
80 |
81 | ```java
82 | KeepaAPI api = new KeepaAPI("YOUR_API_KEY"); // the KeepaAPI object is reuseable
83 | Request r = Request.getProductRequest(AmazonLocale.US, 90, null, "B001GZ6QEC");
84 |
85 | api.sendRequest(r)
86 | .done(result -> {
87 | switch (result.status) {
88 | case OK:
89 | // iterate over received product information
90 | for (Product product : result.products){
91 | // System.out.println(product);
92 | if (product.productType == Product.ProductType.STANDARD.code || product.productType == Product.ProductType.DOWNLOADABLE.code) {
93 |
94 | //get basic data of product and print to stdout
95 | int currentAmazonPrice = ProductAnalyzer.getLast(product.csv[Product.CsvType.AMAZON.index], Product.CsvType.AMAZON);
96 |
97 | //check if the product is in stock -1 -> out of stock
98 | if (currentAmazonPrice == -1) {
99 | System.out.println(product.asin + " " + product.title + " is currently out of stock!");
100 | } else {
101 | System.out.println(product.asin + " " + product.title + " Current Amazon Price: " + currentAmazonPrice);
102 | }
103 |
104 | // get weighted mean of the last 90 days for Amazon
105 | int weightedMean90days = ProductAnalyzer.calcWeightedMean(product.csv[Product.CsvType.AMAZON.index], KeepaTime.nowMinutes(), 90, Product.CsvType.AMAZON);
106 |
107 | ...
108 | } else {
109 | ...
110 | }
111 | }
112 | break;
113 | default:
114 | System.out.println(result);
115 | }
116 | })
117 | .fail(failure -> System.out.println(failure));
118 |
119 | ```
120 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | UTF-8
8 | UTF-8
9 | UTF-8
10 | 1.8
11 | 1.8
12 | github
13 |
14 |
15 | com.keepa.api
16 | backend
17 | 1.97
18 | jar
19 |
20 | Keepa Java Framework
21 | A Java Framework which helps you to deploy your own application, based on the Keepa API.
22 | https://keepa.com/
23 |
24 |
25 |
26 | The Apache License, Version 2.0
27 | http://www.apache.org/licenses/LICENSE-2.0.txt
28 |
29 |
30 |
31 |
32 |
33 | Keepa.com
34 | info@keepa.com
35 | Keepa
36 | https://keepa.com
37 |
38 |
39 |
40 |
41 |
42 |
43 | org.apache.maven.plugins
44 | maven-source-plugin
45 | 3.2.1
46 |
47 |
48 | attach-sources
49 |
50 | jar
51 |
52 |
53 |
54 |
55 |
56 |
57 | org.apache.maven.plugins
58 | maven-javadoc-plugin
59 | 3.4.1
60 |
61 | none
62 | false
63 |
64 |
65 |
66 | attach-javadocs
67 |
68 | jar
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | com.google.code.gson
79 | gson
80 | 2.10
81 |
82 |
83 | org.jdeferred
84 | jdeferred-core
85 | 1.2.6
86 |
87 |
88 | org.slf4j
89 | slf4j-simple
90 | 1.7.21
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/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, 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 | switch (responseCode) {
171 | case 400:
172 | response.status = ResponseStatus.REQUEST_REJECTED;
173 | break;
174 | case 402:
175 | response.status = ResponseStatus.PAYMENT_REQUIRED;
176 | break;
177 | case 405:
178 | response.status = ResponseStatus.METHOD_NOT_ALLOWED;
179 | break;
180 | case 429:
181 | response.status = ResponseStatus.NOT_ENOUGH_TOKEN;
182 | break;
183 | case 500:
184 | response.status = ResponseStatus.INTERNAL_SERVER_ERROR;
185 | break;
186 | default:
187 | if (response.status != ResponseStatus.FAIL)
188 | response.status = ResponseStatus.FAIL;
189 | break;
190 | }
191 | }
192 | } catch (IOException e) {
193 | response = new Response();
194 | response.status = ResponseStatus.FAIL;
195 | response.exception = e;
196 | }
197 |
198 | response.requestTime = (System.nanoTime() - responseTime) / 1000000;
199 | if (response.status == ResponseStatus.OK)
200 | d.resolve(response);
201 | else
202 | d.reject(response);
203 | });
204 | return d.promise();
205 | }
206 |
207 | /**
208 | * Issue a request to the Keepa Price Data API.
209 | * If your API contingent is depleted, this method will retry the request as soon as there are new tokens available. May take minutes.
210 | * Will fail it the request failed too many times.
211 | *
212 | * @param r the API Request {@link Request}
213 | * @param connectTimeout the timeout value, in milliseconds, to be used when opening a connection to the API
214 | * @param readTimeout the read timeout value, in milliseconds, for receiving an API response
215 | * @return Promise for {@link Response}
216 | */
217 | public Promise sendRequestWithRetry(Request r, int connectTimeout, int readTimeout) {
218 | Deferred deferred = new DeferredObject<>();
219 | AtomicInteger expoDelay = new AtomicInteger(0);
220 |
221 | executorRetry.execute(() -> {
222 | int retry = 0;
223 | Response[] lastResponse = {null};
224 | final boolean[] solved = {false};
225 |
226 | try {
227 | int delay = 0;
228 |
229 | while (!solved[0]) {
230 | if (lastResponse[0] != null && lastResponse[0].status == ResponseStatus.NOT_ENOUGH_TOKEN && lastResponse[0].refillIn > 0)
231 | delay = lastResponse[0].refillIn + 100;
232 |
233 | if (retry > 0)
234 | Thread.sleep(delay);
235 |
236 | Promise p1 = sendRequest(r, connectTimeout, readTimeout);
237 | p1
238 | .done(result -> {
239 | deferred.resolve(result);
240 | solved[0] = true;
241 | expoDelay.set(0);
242 | })
243 | .fail(result -> {
244 | lastResponse[0] = result;
245 | switch (result.status) {
246 | case FAIL:
247 | case NOT_ENOUGH_TOKEN: // retry
248 | break;
249 | default:
250 | deferred.reject(result);
251 | solved[0] = true;
252 | }
253 | })
254 | .waitSafely();
255 |
256 | if (p1.isRejected()) {
257 | retry++;
258 | delay = expoDelay.getAndUpdate(operand -> Math.min(2 * operand + 100, maxDelay));
259 | }
260 | }
261 | } catch (InterruptedException e) {
262 | Thread.currentThread().interrupt();
263 | deferred.reject(null);
264 | }
265 | });
266 |
267 | return deferred.promise();
268 | }
269 |
270 | /**
271 | * Issue a request to the Keepa Price Data API.
272 | * If your API contingent is depleted, this method will retry the request as soon as there are new tokens available. May take minutes.
273 | * Will fail it the request failed too many times.
274 | *
275 | * @param r the API Request {@link Request}
276 | * @return Promise for {@link Response}
277 | */
278 | public Promise sendRequestWithRetry(Request r) {
279 | return sendRequestWithRetry(r, 30000, 120000);
280 | }
281 |
282 | }
283 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/exceptions/KeepaAPIException.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.exceptions;
2 |
3 | /**
4 | * API-Exception thrown on different negative API-Events.
5 | */
6 | public class KeepaAPIException extends Exception {
7 | public KeepaAPIException(String s) {
8 | super(s);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/helper/BasicNameFactory.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.helper;
2 |
3 | import java.util.concurrent.ThreadFactory;
4 | import java.util.concurrent.atomic.AtomicInteger;
5 |
6 | /**
7 | * HelperFactory to name ThreadPool-Threads for debugging purpose.
8 | */
9 | public class BasicNameFactory implements ThreadFactory {
10 | private final String namingPattern;
11 | private final AtomicInteger count = new AtomicInteger(0);
12 |
13 | public BasicNameFactory(String s) {
14 | this.namingPattern = s;
15 | }
16 |
17 | @Override
18 | public Thread newThread(Runnable r) {
19 | final Thread t = new Thread(r);
20 | if(this.namingPattern != null) {
21 | t.setName(String.format(namingPattern, count.getAndIncrement()));
22 | }
23 |
24 | return t;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/helper/KeepaTime.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.helper;
2 |
3 | /**
4 | * Keepa Time - Unix Time Converter Helper Class
5 | */
6 | public class KeepaTime {
7 | public static long keepaStartHour = 359400;
8 | public static long keepaStartMinute = 21564000;
9 |
10 | public static int nowHours() {
11 | return unixInMillisToKeepaHour(System.currentTimeMillis());
12 | }
13 |
14 | public static int nowMinutes() {
15 | return unixInMillisToKeepaMinutes(System.currentTimeMillis());
16 | }
17 |
18 | public static int unixInMillisToKeepaMinutes(long unix) {
19 | return (int)((Math.floor(unix / (60 * 1000))) - keepaStartMinute);
20 | }
21 |
22 | public static int unixInMillisToKeepaHour(long unix) {
23 | return (int)((Math.floor(unix / (60 * 60 * 1000))) - keepaStartHour);
24 | }
25 |
26 | public static long keepaHourToUnixInMillis(int hour) {
27 | return hour * 60 * 60 * 1000L + keepaStartHour * 60 * 60 * 1000L;
28 | }
29 |
30 | public static long keepaMinuteToUnixInMillis(int minute) {
31 | return minute * 60 * 1000L + keepaStartMinute * 60 * 1000L;
32 | }
33 |
34 | public static long keepaMinuteToUnixInMillis(String minute) {
35 | return keepaMinuteToUnixInMillis(Integer.parseInt(minute));
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/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/helper/Utility.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.helper;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.GsonBuilder;
5 |
6 | import java.io.UnsupportedEncodingException;
7 | import java.lang.reflect.Modifier;
8 | import java.net.URLEncoder;
9 |
10 | /**
11 | * Static helper methods and reused objects.
12 | */
13 | public class Utility {
14 | public static final Gson gson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.PRIVATE, Modifier.PROTECTED).create();
15 |
16 | public static final Gson gsonPretty = new GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.PRIVATE, Modifier.PROTECTED).setPrettyPrinting().create();
17 |
18 | public static String arrayToCsv(String array[]) {
19 | StringBuilder buff = new StringBuilder();
20 | String sep = "";
21 | for (String s : array) {
22 | buff.append(sep);
23 | buff.append(s);
24 | sep = ",";
25 | }
26 | return buff.toString();
27 | }
28 |
29 | public static String urlEncodeUTF8(String s) {
30 | try {
31 | return URLEncoder.encode(s, "UTF-8");
32 | } catch (UnsupportedEncodingException e) {
33 | throw new UnsupportedOperationException(e);
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/structs/AmazonLocale.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 | /**
4 | * Amazon Locale Domain Enum
5 | */
6 | public enum AmazonLocale {
7 | RESERVED, US, GB, DE, FR, JP, CA, RESERVED2, IT, ES, IN, MX, BR
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/structs/BestSellers.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 | import com.keepa.api.backend.helper.KeepaTime;
4 |
5 | /**
6 | * About:
7 | * A best sellers ASIN list of a specific category.
8 | *
9 | * Returned by:
10 | * Request Best Sellers
11 | */
12 | public class BestSellers {
13 |
14 | /**
15 | * Integer value for the Amazon locale this category belongs to.
16 | * {@link AmazonLocale}
17 | */
18 | public byte domainId;
19 |
20 | /**
21 | * States the last time we have updated the list, in Keepa Time minutes.
22 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
23 | */
24 | public int lastUpdate;
25 |
26 | /**
27 | * 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 or 0 if a product group was used.
28 | */
29 | public long categoryId;
30 |
31 | /**
32 | * An ASIN list. The list starts with the best selling product (lowest sales rank).
33 | */
34 | public String[] asinList;
35 | }
36 |
--------------------------------------------------------------------------------
/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 | * Title of the product. Caution: may contain HTML markup in rare cases.
23 | */
24 | public String title = null;
25 |
26 | /**
27 | * Contains the absolute difference between the current value and the average value of the respective date range interval.
28 | * 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.
29 | *
First dimension uses {@link Product.CsvType}, second domension {@link DealInterval}
30 | */
31 | public int[][] delta = null;
32 |
33 | /**
34 | * Same as {@link #delta}, but given in percent instead of absolute values.
35 | * First dimension uses {@link Product.CsvType}, second domension {@link DealInterval}
36 | */
37 | public short[][] deltaPercent = null;
38 |
39 | /**
40 | * 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.
41 | * Uses {@link Product.CsvType} indexing
42 | */
43 | public int[] deltaLast = null;
44 |
45 | /**
46 | * Contains the weighted averages in the respective date range and price type.
47 | * 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.
48 | * First dimension uses {@link Product.CsvType}, second domension {@link DealInterval}
49 | */
50 | public int[][] avg = null;
51 |
52 | /**
53 | * Contains the prices / ranks of the product of the time we last updated it. Uses the Price Type indexing.
54 | * The price is an integer of the respective Amazon locale's smallest currency unit (e.g. euro cents or yen).
55 | * If no offer was available in the given interval (e.g. out of stock) the price has the value -1.
56 | * Shipping and Handling costs are not included. Amazon is considered to be part of the marketplace, so if
57 | * Amazon has the overall lowest new price, the marketplace new price in the corresponding time interval will
58 | * be identical to the Amazon price (except if there is only one marketplace offer).
59 | * Uses {@link Product.CsvType} indexing
60 | */
61 | public int[] current = null;
62 |
63 | /**
64 | * Category node id {@link Category#catId} of the product's root category. 0 or 9223372036854775807 if no root category known.
65 | */
66 | public long rootCat = 0L;
67 |
68 | /**
69 | * States the time this deal was found, in Keepa Time minutes.
70 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
71 | */
72 | public int creationDate = 0;
73 |
74 | /**
75 | * The name of the main product image of the product. Make sure you own the rights to use the image.
76 | * 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);.
77 | * Example: [54,49,107,51,76,97,121,55,74,85,76,46,106,112,103], which equals "61k3Lay7JUL.jpg".
78 | * Full Amazon image path: https://m.media-amazon.com/images/I/_image name_
79 | */
80 | public byte[] image = null;
81 |
82 | /**
83 | * Array of Amazon category node ids {@link Category#catId} this product is listed in. Can be empty.
84 | * Example: [569604]
85 | */
86 | public long[] categories = null;
87 |
88 | /**
89 | * States the last time we have updated the information for this deal, in Keepa Time minutes.
90 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
91 | */
92 | public int lastUpdate = 0;
93 |
94 | /**
95 | * States the time this lightning deal is scheduled to end, in Keepa Time minutes. Only applicable to lightning deals. 0 otherwise.
96 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
97 | */
98 | public int lightningEnd = 0;
99 |
100 | /**
101 | * Limit to products with a minimum rating (A rating is an integer from 0 to 50 (e.g. 45 = 4.5 stars)).
102 | * If -1 the filter is inactive.
103 | * Example: 20 (= min. rating of 2 stars)
104 | */
105 | public int minRating = -1;
106 |
107 | /**
108 | * The {@link OfferCondition} condition of the cheapest warehouse deal of this product. Integer value:
109 | *
0 - Unknown: We were unable to determine the condition or this is not a warehouse deal in our data
110 | *
2 - Used - Like New
111 | *
3 - Used - Very Good
112 | *
4 - Used - Good
113 | *
5 - Used - Acceptable
114 | */
115 | public byte warehouseCondition;
116 |
117 | /**
118 | * The offer comment of the cheapest warehouse deal of this product. null if no warehouse deal found in our data.
119 | */
120 | public String warehouseConditionComment;
121 |
122 | /**
123 | * The timestamp indicating the starting point from which the current value has been in effect, in Keepa Time minutes.
124 | * Uses {@link Product.CsvType} indexing.
125 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
126 | */
127 | public int[] currentSince;
128 |
129 |
130 | /**
131 | * Available deal ranges.
132 | */
133 | public enum DealInterval {
134 | DAY,
135 | WEEK,
136 | MONTH,
137 | _90_DAYS;
138 | public static final DealInterval[] values = DealInterval.values();
139 | }
140 |
141 | @Override
142 | public String toString() {
143 | return gson.toJson(this);
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/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 | *
150 | */
151 | public String categorySearch;
152 |
153 | public boolean isPrimeExclusive;
154 | public boolean mustHaveAmazonOffer;
155 | public boolean mustNotHaveAmazonOffer;
156 | public Integer minRating;
157 | public Byte[] warehouseConditions;
158 | public Boolean singleVariation;
159 |
160 | public String[] material;
161 | public String[] type;
162 | public String[] manufacturer;
163 | public String[] brand;
164 | public String[] productGroup;
165 | public String[] model;
166 | public String[] color;
167 | public String[] size;
168 | public String[] unitType;
169 | public String[] scent;
170 | public String[] itemForm;
171 | public String[] pattern;
172 | public String[] style;
173 | public String[] itemTypeKeyword;
174 | public String[] targetAudienceKeyword;
175 | public String[] edition;
176 | public String[] format;
177 | public String[] author;
178 | public String[] binding;
179 | public String[] languages;
180 |
181 | @Override
182 | public String toString() {
183 | return gsonPretty.toJson(this);
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/structs/DealResponse.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 | import static com.keepa.api.backend.helper.Utility.gson;
4 |
5 | /**
6 | * The response of a browse deals request.
7 | * Each deal product is listed in one root category. The three fields categoryIds, categoryNames and categoryCount contain information about those categories. The values of the same index in those arrays belong together, so the first category entry would have the id categoryIds[0], the name categoryNames[0] and the deals count categoryCount[0]. If the root category of a product could not be determined it will be listed in the category with the name "?" with the id 9223372036854775807.
8 | */
9 | public class DealResponse {
10 |
11 | /**
12 | * Ordered array of all deal objects matching your query.
13 | */
14 | public Deal[] dr = null;
15 |
16 | /**
17 | * Not yet used / placeholder
18 | */
19 | public byte[] drDateIndex = null;
20 |
21 | /**
22 | * Contains all root categoryIds of the matched deal products.
23 | */
24 | public long[] categoryIds = null;
25 |
26 | /**
27 | * Contains all root category names of the matched deal products.
28 | */
29 | public String[] categoryNames = null;
30 |
31 | /**
32 | * Contains how many deal products in the respective root category are found.
33 | */
34 | public int[] categoryCount = null;
35 |
36 | @Override
37 | public String toString() {
38 | return gson.toJson(this);
39 | }
40 | }
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/structs/LightningDeal.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 | public class LightningDeal {
4 | /**
5 | * The domainId of the products Amazon locale
6 | * {@link AmazonLocale}
7 | */
8 | public byte domainId;
9 |
10 | /**
11 | * States the time of our last data collection of this lighting deal, in Keepa Time minutes.
12 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
13 | */
14 | public int lastUpdate;
15 |
16 | /**
17 | * The ASIN of the product
18 | */
19 | public String asin;
20 |
21 | /**
22 | * Title of the product. Caution: may contain HTML markup in rare cases.
23 | */
24 | public String title;
25 |
26 | /**
27 | * The seller id of the merchant offering this deal.
28 | */
29 | public String sellerName;
30 |
31 | /**
32 | * The name of seller offering this deal.
33 | */
34 | public String sellerId;
35 |
36 | /**
37 | * A unique ID for this deal.
38 | */
39 | public String dealId;
40 |
41 | /**
42 | * The discounted price of this deal. Available once the deal has started. -1 if the deal’s state is upcoming. The price is an integer of the respective Amazon locale’s smallest currency unit (e.g. euro cents or yen).
43 | */
44 | public int dealPrice;
45 |
46 | /**
47 | * The regular price of this product. Available once the deal has started. -1 if the deal’s state is upcoming. The price is an integer of the respective Amazon locale’s smallest currency unit (e.g. euro cents or yen).
48 | */
49 | public int currentPrice;
50 |
51 | /**
52 | * The name of the primary image of the product. null if not available.
53 | */
54 | public String image;
55 |
56 | /**
57 | * Whether or not the deal is Prime eligible.
58 | */
59 | public boolean isPrimeEligible;
60 |
61 | /**
62 | * Whether or not the deal is fulfilled by Amazon.
63 | */
64 | public boolean isFulfilledByAmazon;
65 |
66 | /**
67 | * Whether or not the price is restricted by MAP (Minimum Advertised Price).
68 | */
69 | public boolean isMAP;
70 |
71 | /**
72 | * The rating of the product. A rating is an integer from 0 to 50 (e.g. 45 = 4.5 stars).
73 | */
74 | public int rating;
75 |
76 | /**
77 | * The product’s review count.
78 | */
79 | public int totalReviews;
80 |
81 | /**
82 | * The state of the deal.
83 | */
84 | public DealState dealState;
85 |
86 | /**
87 | * The start time of this lighting deal, in Keepa Time minutes. Note that due to the delay in our data collection the deal price might not be available immediately once the deal has started on Amazon.
88 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
89 | */
90 | public int startTime;
91 |
92 | /**
93 | * The end time of this lighting deal, in Keepa Time minutes.
94 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
95 | */
96 | public int endTime;
97 |
98 | /**
99 | * The percentage claimed of the lighting deal. Since lightning deals have limited stock, this number may change fast on Amazon, but due to the delay of our data collection the provided value may be outdated.
100 | */
101 | public int percentClaimed;
102 |
103 | /**
104 | * The provided discount of this deal, according to Amazon. May be in reference to the list price, not the current price.
105 | */
106 | public int percentOff;
107 |
108 | /**
109 | * The dimension attributes of this deal.
110 | */
111 | public Product.VariationAttributeObject[] variation;
112 |
113 |
114 | public enum DealState {
115 | AVAILABLE, UPCOMING, WAITLIST, SOLDOUT, WAITLISTFULL, EXPIRED, SUPPRESSED
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/structs/Notification.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 | import com.keepa.api.backend.helper.KeepaTime;
4 |
5 | /**
6 | * Represents a price alert
7 | */
8 | public class Notification {
9 |
10 | /**
11 | * The notified product ASIN
12 | */
13 | public String asin = null;
14 |
15 | /**
16 | * Title of the product. Caution: may contain HTML markup in rare cases.
17 | */
18 | public String title;
19 |
20 | /**
21 | * The main image name of the product. Full Amazon image path:
22 | * https://m.media-amazon.com/images/I/_image name_
23 | */
24 | public String image;
25 |
26 | /**
27 | * Creation date of the notification in {@link KeepaTime} minutes
28 | */
29 | public int createDate;
30 |
31 | /**
32 | * The main Amazon locale of the tracking which determines the currency used for all prices of this notification.
33 | * Integer value for the Amazon locale {@link AmazonLocale}
34 | */
35 | public byte domainId;
36 |
37 | /**
38 | * The Amazon locale which triggered the notification.
39 | * Integer value for the Amazon locale {@link AmazonLocale}
40 | */
41 | public byte notificationDomainId;
42 |
43 | /**
44 | * The {@link Product.CsvType} which triggered the notification.
45 | */
46 | public int csvType;
47 |
48 | /**
49 | * The {@link Tracking.TrackingNotificationCause} of the notification.
50 | */
51 | public int trackingNotificationCause;
52 |
53 | /**
54 | * Contains the prices / values of the product of the time this notification was created.
55 | * Uses {@link Product.CsvType} indexing.
56 | * The price is an integer of the respective Amazon locale's smallest currency unit (e.g. euro cents or yen).
57 | * If no offer was available in the given interval (e.g. out of stock) the price has the value -1.
58 | */
59 | public int[] currentPrices;
60 |
61 | /**
62 | * States through which notification channels ({@link Tracking.NotificationType}) this notification was delivered.
63 | */
64 | public boolean[] sentNotificationVia;
65 |
66 | /**
67 | * The meta data of the tracking.
68 | */
69 | public String metaData;
70 | }
71 |
--------------------------------------------------------------------------------
/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 | public enum OfferCondition {
182 | UNKNOWN(0),
183 | NEW(1),
184 | USED_NEW(2), USED_VERY_GOOD(3), USED_GOOD(4), USED_ACCEPTABLE(5),
185 | REFURBISHED(6),
186 | COLLECTIBLE_NEW(7), COLLECTIBLE_VERY_GOOD(8), COLLECTIBLE_GOOD(9), COLLECTIBLE_ACCEPTABLE(10);
187 |
188 | public final byte code;
189 | public static final OfferCondition[] values = OfferCondition.values();
190 |
191 | public static Product.CsvType getCorrespondingCsvType(OfferCondition oc) {
192 | Product.CsvType type = null;
193 | switch (oc) {
194 | case UNKNOWN:
195 | case NEW:
196 | break;
197 | case USED_NEW:
198 | type = Product.CsvType.USED_NEW_SHIPPING;
199 | break;
200 | case USED_VERY_GOOD:
201 | type = Product.CsvType.USED_VERY_GOOD_SHIPPING;
202 | break;
203 | case USED_GOOD:
204 | type = Product.CsvType.USED_GOOD_SHIPPING;
205 | break;
206 | case USED_ACCEPTABLE:
207 | type = Product.CsvType.USED_ACCEPTABLE_SHIPPING;
208 | break;
209 | case REFURBISHED:
210 | type = Product.CsvType.REFURBISHED_SHIPPING;
211 | break;
212 | case COLLECTIBLE_NEW:
213 | type = Product.CsvType.COLLECTIBLE_NEW_SHIPPING;
214 | break;
215 | case COLLECTIBLE_VERY_GOOD:
216 | type = Product.CsvType.COLLECTIBLE_VERY_GOOD_SHIPPING;
217 | break;
218 | case COLLECTIBLE_GOOD:
219 | type = Product.CsvType.COLLECTIBLE_GOOD_SHIPPING;
220 | break;
221 | case COLLECTIBLE_ACCEPTABLE:
222 | type = Product.CsvType.COLLECTIBLE_ACCEPTABLE_SHIPPING;
223 | break;
224 | }
225 |
226 | return type;
227 | }
228 |
229 | OfferCondition(int index) {
230 | code = (byte) index;
231 | }
232 | }
233 |
234 | @Override
235 | public String toString() {
236 | return gson.toJson(this);
237 | }
238 | }
239 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/structs/Product.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 |
4 | import com.keepa.api.backend.helper.KeepaTime;
5 |
6 | import java.util.HashMap;
7 |
8 | import static com.keepa.api.backend.helper.Utility.gson;
9 |
10 | public final class Product {
11 |
12 | /**
13 | * The ASIN of the product
14 | */
15 | public String asin = null;
16 |
17 | /**
18 | * The domainId of the products Amazon locale
19 | * {@link AmazonLocale}
20 | */
21 | public byte domainId = 0;
22 |
23 | /**
24 | * The ASIN of the parent product (if the product has variations, otherwise null)
25 | */
26 | public String parentAsin = null;
27 |
28 | /**
29 | * The history of the parentAsin field. This array follows the format: [Keepa time in minutes, previous parent ASIN, ...].
30 | * The included timestamp indicates when the previously associated parent ASIN ceased to be valid.
31 | * For the current parent ASIN, use the parentAsin field.
32 | * To convert a Keepa minute timestamp into an uncompressed, Unix epoch time timestamp, use the {@link KeepaTime#keepaMinuteToUnixInMillis(int)} method.
33 | * null if the parentAsin field never changed.
34 | */
35 | public String[] parentAsinHistory = null;
36 |
37 | /**
38 | * Comma separated list of variation ASINs of the product (if the product is a parent ASIN, otherwise null)
39 | * @deprecated use the field variations instead
40 | */
41 | public String variationCSV = null;
42 |
43 | /**
44 | * A list of UPC assigned to this product. The first index is the primary UPC. null if not available.
45 | */
46 | public String[] upcList = null;
47 |
48 | /**
49 | * A list of EAN assigned to this product. The first index is the primary EAN. null if not available.
50 | */
51 | public String[] eanList = null;
52 |
53 | /**
54 | * A list of GTIN assigned to this product. The first index is the primary GTIN. null if not available.
55 | */
56 | public String[] gtinList = null;
57 |
58 | /**
59 | * Comma separated list of image names of the product. Full Amazon image path:
60 | * https://m.media-amazon.com/images/I/_image name_
61 | * @deprecated use the field images instead
62 | */
63 | public String imagesCSV = null;
64 |
65 | /**
66 | * Provides metadata for images associated with the product.
67 | *
68 | * This field is an array of {@link Image} objects, each of which may contain metadata for up to two resolutions
69 | * (large and medium) of the same image, if available. These images are typically used in the product image carousel
70 | * on platforms such as Amazon. This field is {@code null} if no image data is available.
71 | *
72 | * Each {@code Image} object includes:
73 | *
74 | * - {@code l} (String): Filename of the large image.
75 | * - {@code lH} (Short): Height of the large image, in pixels.
76 | * - {@code lW} (Short): Width of the large image, in pixels.
77 | * - {@code m} (String): Filename of the medium image.
78 | * - {@code mH} (Short): Height of the medium image, in pixels.
79 | * - {@code mW} (Short): Width of the medium image, in pixels.
80 | *
81 | *
82 | * Example:
83 | *
{@code
84 | * "images": [{
85 | * "l": "81bRlmLRyPL.jpg",
86 | * "lH": 1500,
87 | * "lW": 1500,
88 | * "m": "g1dlEmb2mq3.jpg",
89 | * "mH": 500,
90 | * "mW": 500
91 | * }]
92 | * }
93 | *
94 | * Full Amazon image path format: {@code https://m.media-amazon.com/images/I/}
95 | */
96 | public Image[] images = null;
97 |
98 | /**
99 | * Array of category node ids
100 | */
101 | public long[] categories = null;
102 |
103 | /**
104 | * Category node id of the products' root category. 0 if no root category known
105 | */
106 | public long rootCategory = 0L;
107 |
108 | /**
109 | * Name of the manufacturer
110 | */
111 | public String manufacturer = null;
112 |
113 | /**
114 | * Title of the product. Caution: may contain HTML markup in rare cases.
115 | */
116 | public String title = null;
117 |
118 | /**
119 | * States the time we have started tracking this product, in Keepa Time minutes.
120 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
121 | */
122 | public int trackingSince = 0;
123 |
124 | /**
125 | * States the time the item was first listed on Amazon, in Keepa Time minutes.
126 | * It is updated in conjunction with the offers request, but always accessible.
127 | * This timestamp is only available for some products. If not available the field has the value 0.
128 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
129 | */
130 | public int listedSince = 0;
131 |
132 | /**
133 | * An item's brand. null if not available.
134 | */
135 | public String brand = null;
136 |
137 | /**
138 | * The item's productGroup. null if not available.
139 | */
140 | public String productGroup = null;
141 |
142 | /**
143 | * The item's partNumber. null if not available.
144 | */
145 | public String partNumber = null;
146 |
147 | /**
148 | * The item's model. null if not available.
149 | */
150 | public String model = null;
151 |
152 | /**
153 | * The item's color. null if not available.
154 | */
155 | public String color = null;
156 |
157 | /**
158 | * The item's size. null if not available.
159 | */
160 | public String size = null;
161 |
162 | /**
163 | * The item's edition. null if not available.
164 | */
165 | public String edition = null;
166 |
167 | /**
168 | * The item's format. null if not available.
169 | */
170 | public String format = null;
171 |
172 | /**
173 | * The item’s author. null if not available.
174 | *
175 | * @deprecated use the field contributors instead
176 | */
177 | public String author = null;
178 |
179 | /**
180 | * The item’s binding. null if not available. If the item is not a book it is usually the product category instead.
181 | */
182 | public String binding = null;
183 |
184 | /**
185 | * Represents the category tree as an ordered array of CategoryTreeEntry objects.
186 | */
187 | public CategoryTreeEntry[] categoryTree = null;
188 |
189 | /**
190 | * The number of items of this product. -1 if not available.
191 | */
192 | public int numberOfItems = -1;
193 |
194 | /**
195 | * The number of pages of this product. -1 if not available.
196 | */
197 | public int numberOfPages = -1;
198 |
199 | /**
200 | * The item’s publication date in one of the following three formats:
201 | * YYYY or YYYYMM or YYYYMMDD (Y= year, M = month, D = day)
202 | * -1 if not available.
203 | * Examples:
204 | * 1978 = the year 1978
205 | * 200301 = January 2003
206 | * 20150409 = April 9th, 2015
207 | */
208 | public int publicationDate = -1;
209 |
210 | /**
211 | * The item’s release date in one of the following three formats:
212 | * YYYY or YYYYMM or YYYYMMDD (Y= year, M = month, D = day)
213 | * -1 if not available.
214 | * Examples:
215 | * 1978 = the year 1978
216 | * 200301 = January 2003
217 | * 20150409 = April 9th, 2015
218 | */
219 | public int releaseDate = -1;
220 |
221 | /**
222 | * An item can have one or more languages. Each language entry has a name and a type.
223 | * Some also have an audio format. null if not available.
224 | * Examples:
225 | * [ [ “English”, “Published” ], [ “English”, “Original Language” ] ]
226 | * With audio format:
227 | * [ [ “Englisch”, “Originalsprache”, “DTS-HD 2.0” ], [ “Deutsch”, null, “DTS-HD 2.0” ] ]
228 | */
229 | public String[][] languages = null;
230 |
231 | /**
232 | * The contributors of the item. A contributor can be an author, actor, director, etc. Each contributor entry has a name and a role type.
233 | * Example:
234 | * [ [ “Vin Diesel”, “actor” ] ]
235 | */
236 | public String[][] contributors = null;
237 |
238 | /**
239 | * A list of the product features / bullet points. null if not available.
240 | * An entry can contain HTML markup in rare cases. We currently limit each entry to a maximum of 1000 characters
241 | * (if the feature is longer it will be cut off). This limitation may change in the future without prior notice.
242 | */
243 | public String[] features = null;
244 |
245 | /**
246 | * A description of the product. null if not available. Most description contain HTML markup.
247 | * We limit the product description to a maximum of 2000 characters (if the description is
248 | * longer it will be cut off). This limitation may change in the future without prior notice.
249 | */
250 | public String description = null;
251 |
252 | /**
253 | * The package's height in millimeter. 0 or -1 if not available.
254 | */
255 | public int packageHeight = -1;
256 |
257 | /**
258 | * The package's length in millimeter. 0 or -1 if not available.
259 | */
260 | public int packageLength = -1;
261 |
262 | /**
263 | * The package's width in millimeter. 0 or -1 if not available.
264 | */
265 | public int packageWidth = -1;
266 |
267 | /**
268 | * The package's weight in gram. 0 or -1 if not available.
269 | */
270 | public int packageWeight = -1;
271 |
272 | /**
273 | * Quantity of items in a package. 0 or -1 if not available.
274 | */
275 | public int packageQuantity = -1;
276 |
277 | /**
278 | * The item's height in millimeter. 0 or -1 if not available.
279 | */
280 | public int itemHeight = -1;
281 |
282 | /**
283 | * The item's length in millimeter. 0 or -1 if not available.
284 | */
285 | public int itemLength = -1;
286 |
287 | /**
288 | * The item's width in millimeter. 0 or -1 if not available.
289 | */
290 | public int itemWidth = -1;
291 |
292 | /**
293 | * The item's weight in gram. 0 or -1 if not available.
294 | */
295 | public int itemWeight = -1;
296 |
297 | /**
298 | * Contains the lowest priced matching eBay listing Ids.
299 | * Always contains two entries, the first one is the listing id of the lowest priced listing in new condition,
300 | * the second in used condition. null or 0 if not available.
301 | * Example: [ 273344490183, 0 ]
302 | */
303 | public long[] ebayListingIds = null;
304 |
305 | /**
306 | * Indicates if the item is considered to be for adults only.
307 | */
308 | public boolean isAdultProduct = false;
309 |
310 | /**
311 | * Whether or not the product is eligible for trade-in.
312 | */
313 | public boolean isEligibleForTradeIn = false;
314 |
315 | /**
316 | * @deprecated use the field referralFeePercentage instead
317 | */
318 | public Integer referralFeePercent = null;
319 |
320 | /**
321 | * The variable closing fee. Fees are integers of the respective Amazon locale’s smallest currency unit (e.g. euro cents or yen). null if not available.
322 | * Example: 81
323 | */
324 | public Integer variableClosingFee = null;
325 |
326 | /**
327 | * The product listing URL slug.
328 | * Example: Ring-Video-Doorbell-Satin-Nickel-2020-Release
329 | */
330 | public String urlSlug = null;
331 |
332 | /**
333 | * The ingredient list of the product. null if not available.
334 | * Example: Purified Carbonated Water, Natural Flavors
335 | */
336 | public String ingredients = null;
337 |
338 | /**
339 | * True if this product is an Amazon Haul product. null otherwise.
340 | * Example: true
341 | */
342 | public Boolean isHaul;
343 |
344 | /**
345 | * The referral fee percent is determined by either the current price or, in the absence of a current offer, the previous one. If neither of these prices is available for reference, the fee percent is calculated based on a standard sales price of 100.00. *null* if not available.
346 | * Example: 12
347 | */
348 | public Double referralFeePercentage = null;
349 |
350 | /**
351 | * States the last time we have updated the monthlySold field, in Keepa Time minutes. null if the monthlySold has no value.
352 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
353 | */
354 | public int lastSoldUpdate = 0;
355 |
356 | /**
357 | * How often this product was bought in the past month. This field represents the bought past month metric found on Amazon search result pages. It is not an estimate. null if it has no value. Most ASINs do not have this value set. The value is variation specific.
358 | * Example: 1000 - the ASIN was bought at least 1000 times in the past month.
359 | */
360 | public int monthlySold = 0;
361 |
362 | /**
363 | * Contains historical values of the monthlySold field. null if it has no value.
364 | */
365 | public int[] monthlySoldHistory = null;
366 |
367 | /**
368 | * Whether or not the product is eligible for super saver shipping by Amazon (not FBA).
369 | */
370 | public boolean isEligibleForSuperSaverShipping = false;
371 |
372 | /**
373 | * States the last time we have updated the information for this product, in Keepa Time minutes.
374 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
375 | */
376 | public int lastUpdate = 0;
377 |
378 | /**
379 | * States the last time we have registered a price change (any price kind), in Keepa Time minutes.
380 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} to get an uncompressed timestamp (Unix epoch time).
381 | */
382 | public int lastPriceChange = 0;
383 |
384 | /**
385 | * States the last time we have updated the eBay prices for this product, in Keepa Time minutes.
386 | * If no matching products were found the integer is negative.
387 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
388 | */
389 | public int lastEbayUpdate = 0;
390 |
391 | /**
392 | * The most recent update of the stock data for this product’s offers, in Keepa Time minutes.
393 | * Has the value 0 unless the stock parameter was used and stock data was collected at least once.
394 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
395 | */
396 | public int lastStockUpdate = 0;
397 |
398 | /**
399 | * States the last time we have updated the product rating and review count, in Keepa Time minutes.
400 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
401 | */
402 | public int lastRatingUpdate = 0;
403 |
404 | /**
405 | * Keepa product type {@link Product.ProductType}. Must always be evaluated first.
406 | */
407 | public byte productType = 0;
408 |
409 | /**
410 | * The item’s type. null if not available.
411 | */
412 | public String type = null;
413 |
414 | /**
415 | * Whether or not the product has reviews.
416 | */
417 | public boolean hasReviews = false;
418 |
419 | /**
420 | * Optional field. Only set if the stats parameter was used in the Product Request. Contains statistic values.
421 | */
422 | public Stats stats = null;
423 |
424 | /**
425 | * Optional field. Only set if the offers parameter was used in the Product Request.
426 | */
427 | public Offer[] offers = null;
428 |
429 | /**
430 | * Optional field. Only set if the offers parameter was used in the Product Request.
431 | * Contains an ordered array of index positions in the offers array for all Marketplace Offer Objects114 retrieved for this call.
432 | * The sequence of integers reflects the ordering of the offers on the Amazon offer page (for all conditions).
433 | * Since the offers field contains historical offers as well as current offers, one can use this array to
434 | * look up all offers that are currently listed on Amazon in the correct order.
435 | * Example:
[ 3, 5, 2, 18, 15 ] - The offer with the array index 3 of the offers field is currently the first
436 | * one listed on the offer listings page on Amazon, followed by the offer with the index 5, and so on.
437 | * Example with duplicates:
[ 3, 5, 2, 18, 5 ] - The second offer, as listed on Amazon, is a lower priced duplicate
438 | * of the 6th offer on Amazon. The lower priced one is included in the offers field at index 5.
439 | */
440 | public int[] liveOffersOrder = null;
441 |
442 | /**
443 | * Optional field. Only set if the offers parameter was used in the Product Request.
444 | * Contains a history of sellerIds that held the Buy Box in the format Keepa time minutes, sellerId, [...].
445 | * If no seller qualified for the Buy Box the sellerId "-1" is used. If it was hold by an unknown seller (a brand new one) the sellerId is "-2".
446 | * Example: ["2860926","ATVPDKIKX0DER", …]
447 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(String)} (long)} to get an uncompressed timestamp (Unix epoch time).
448 | */
449 | public String[] buyBoxSellerIdHistory = null;
450 |
451 | /**
452 | * Optional field. Only set if the offers or buybox parameter was used in the Product Request.
453 | * A history of the used buy box winners, containing the sellerIds 159, offer sub-condition and FBA status in the format:
454 | * Keepa time minutes, seller id, condition, isFBA, […].
455 | * If no seller qualified for the used buy box the sellerId "" (empty String) is used.
456 | *
457 | * condition can have the following values:
458 | * “2” - Used - Like New, “3” - Used - Very Good, “4” - Used - Good, “5” - Used - Acceptable
459 | * isFBA is either “1” - offer is FBA or “0” - offer is merchant fulfilled.
460 | * Example: [“2860926”, “ATVPDKIKX0DER”, “4”, “1”, …]
461 | *
Use {@link KeepaTime#keepaMinuteToUnixInMillis(String)} (long)} to get an uncompressed timestamp (Unix epoch time).
462 | */
463 | public String[] buyBoxUsedHistory = null;
464 |
465 | /**
466 | * Only valid if the offers parameter was used in the Product Request.
467 | * Boolean indicating if the ASIN will be redirected to another one on Amazon
468 | * (example: the ASIN has the color black variation, which is not available any more
469 | * and is redirected on Amazon to the color red).
470 | */
471 | public boolean isRedirectASIN = false;
472 |
473 | /**
474 | * Only valid if the offers parameter was used in the Product Request. Boolean indicating if the product's Buy Box is available for subscribe and save.
475 | */
476 | public boolean isSNS = false;
477 |
478 | /**
479 | * Suggested Lower Price for the Buy Box, if the buy box is suppressed.
480 | */
481 | public Integer suggestedLowerPrice = null;
482 |
483 |
484 | /**
485 | * Competitive Price Threshold (CPT) for the Buy Box, if the buy box is suppressed.
486 | */
487 | public Integer competitivePriceThreshold = null;
488 |
489 | /**
490 | * If buyBoxEligibleOfferCounts is available, it represents an array of integers, each entry indicating the total number of offers eligible for the Buy Box across specified offer conditions and fulfillment channels. This array contains eight elements, indexed as follows:
491 | * 0: New FBA
492 | * 1: New FBM
493 | * 2: Used FBA
494 | * 3: Used FBM
495 | * 4: Collectible FBA
496 | * 5: Collectible FBM
497 | * 6: Refurbished FBA
498 | * 7: Refurbished FBM
499 | */
500 | public int[] buyBoxEligibleOfferCounts = null;
501 |
502 | /**
503 | * The hazardous material type of this product, if applicable.
504 | */
505 | public HazardousMaterial[] hazardousMaterials = null;
506 |
507 | /**
508 | * Only valid if the offers parameter was used in the Product Request. Boolean indicating if the system was able to retrieve fresh offer information.
509 | */
510 | public boolean offersSuccessful = false;
511 |
512 | /**
513 | * One or two “Frequently Bought Together” ASINs. null if not available. Field is updated when the offers parameter was used.
514 | */
515 | public String[] frequentlyBoughtTogether = null;
516 |
517 | /**
518 | * True if this product is an Amazon Merch on Demand product
519 | */
520 | public Boolean isMerchOnDemand = null;
521 |
522 | /**
523 | * Indicates if the item is heat sensitive (e.g. meltable).
524 | */
525 | public Boolean isHeatSensitive = null;
526 |
527 | /**
528 | * Indicates the return rate of this product.
529 | * - `null` if the return rate is unavailable or average.
530 | * - `1` for a low return rate.
531 | * - `2` for a high return rate.
532 | */
533 | public Byte returnRate = null;
534 |
535 | /**
536 | * Contains current promotions for this product. Only Amazon US promotions by Amazon (not 3rd party) are collected. In rare cases data can be incomplete.
537 | */
538 | public PromotionObject[] promotions = null;
539 |
540 | /**
541 | * Contains the dimension attributes for up to 50 variations of this product. Only available on parent ASINs.
542 | */
543 | public VariationObject[] variations = null;
544 |
545 | /**
546 | * Availability of the Amazon offer {@link Product.AvailabilityType}.
547 | */
548 | public int availabilityAmazon = -1;
549 |
550 | /**
551 | * Contains coupon details if any are available for the product. null if not available.
552 | * Integer array with always two entries: The first entry is the discount of a one time coupon, the second is a subscribe and save coupon.
553 | * Entry value is either 0 if no coupon of that type is offered, positive for an absolute discount or negative for a percentage discount.
554 | * The coupons field is always accessible, but only updated if the offers parameter was used in the Product Request.
555 | * Example:
556 | * [ 200, -15 ] - Both coupon types available: $2 one time discount or 15% for subscribe and save.
557 | * [ -7, 0 ] - Only one time coupon type is available offering a 7% discount.
558 | *
559 | */
560 | public int[] coupon = null;
561 |
562 | /**
563 | * Whether or not the current new price is MAP restricted. Can be used to differentiate out of stock vs. MAP restricted prices (as in both cases the current price is -1).
564 | */
565 | public boolean newPriceIsMAP = false;
566 |
567 | /**
568 | * FBA fees for this product. If FBA fee information has not yet been collected for this product the field will be null.
569 | */
570 | public FBAFeesObject fbaFees = null;
571 |
572 | /**
573 | * Contains subcategory rank histories. Each key represents the categoryId of the rank with the history in the corresponding value.
574 | */
575 | public HashMap salesRanks = null;
576 |
577 | /**
578 | * The category node id of the main sales rank. -1 if not available.
579 | */
580 | public long salesRankReference = -1;
581 |
582 | /**
583 | * The category node id history of the main sales rank (format: timestamp, categoryId, […]). null if not available.
584 | */
585 | public long[] salesRankReferenceHistory = null;
586 |
587 |
588 | /**
589 | * Rental details
590 | */
591 | public String rentalDetails = null;
592 |
593 | /**
594 | * Rental prices
595 | */
596 | public RentalObject rentalPrices = null;
597 |
598 | /**
599 | * Rental seller id
600 | */
601 | public String rentalSellerId = null;
602 |
603 |
604 | /**
605 | * Amazon offer shipping delay. Integer array with 2 entries, indicating min and max shipping delay in hours.
606 | */
607 | public int[] availabilityAmazonDelay = null;
608 |
609 | /**
610 | * Audience rating. The rating suggests the age for which the media is appropriate.
611 | * Example: PG-13 (Parents Strongly Cautioned)
612 | */
613 | public String audienceRating = null;
614 |
615 | /**
616 | * Unit Count information
617 | */
618 | public UnitCountObject unitCount = null;
619 |
620 | /**
621 | * The scent of the product. Describes the fragrance associated with the product.
622 | * Example: "Lavender"
623 | */
624 | public String scent = null;
625 |
626 | /**
627 | * A brief description of the product.
628 | * Example: "A soothing lavender-scented candle."
629 | */
630 | public String shortDescription = null;
631 |
632 | /**
633 | * Active ingredients present in the product.
634 | * Example: "Lavender essential oil, Soy wax"
635 | */
636 | public String activeIngredients = null;
637 |
638 | /**
639 | * Special ingredients used in the product that may have unique properties.
640 | * Example: "Beeswax blend, Natural dyes"
641 | */
642 | public String specialIngredients = null;
643 |
644 | /**
645 | * The form or physical state of the item.
646 | * Example: "Liquid", "Solid", "Gel"
647 | */
648 | public String itemForm = null;
649 |
650 | /**
651 | * Keywords describing the type or category of the item.
652 | * Example: "body-lotions"
653 | */
654 | public String itemTypeKeyword = null;
655 |
656 | /**
657 | * Recommended uses for the product to guide customers.
658 | * Example: "Aromatherapy, Home Decoration"
659 | */
660 | public String recommendedUsesForProduct = null;
661 |
662 | /**
663 | * The pattern or design featured on the product.
664 | * Example: "Striped", "Floral"
665 | */
666 | public String pattern = null;
667 |
668 | /**
669 | * The store name of the item’s brand. null if not available.
670 | * Example: Hot Wheels
671 | */
672 | public String brandStoreName = null;
673 |
674 | /**
675 | * The brand store URL path. null if not available. To get the full URL, prepend the Amazon domain of the respective locale (e.g. https//www.amazon.com).
676 | * Example: /stores/LEGO/page/017EF856-965D-4B56-A171-EA61CAFF45DD
677 | */
678 | public String brandStoreUrl = null;
679 |
680 | /**
681 | * The brand store Name from the URL path. null if not available.
682 | * Example: LEGO (from the URL: /stores/LEGO/page/017EF856-965D-4B56-A171-EA61CAFF45DD)
683 | */
684 | public String brandStoreUrlName = null;
685 |
686 | /**
687 | * Provides metadata for videos associated with the product.
688 | *
689 | * The {@code videos} parameter is mandatory for access. Each object in the array represents
690 | * the metadata for a single video. Metadata can be retrieved for all image carousel videos
691 | * and up to 10 community videos from the product listing’s Videos section. To request live
692 | * data for this field, the {@code offers} parameter must also be included. Returns {@code null}
693 | * if unavailable.
694 | *
695 | * Example:
696 | *
697 | * "videos": [{
698 | * "title": "Compressed Air Duster",
699 | * "image": "31XBcVI7oTL.jpg",
700 | * "duration": 36,
701 | * "creator": "Seller",
702 | * "name": "Innovation",
703 | * "url": "https://m.media-amazon.com/images/S/vse-vms-transcoding-artifact-us-east-1-prd/d8d8f97e-aa42-42d2-aa4c-6ab1b006edb5/default.jobtemplate.hls.m3u8"
704 | * }]
705 | *
706 | */
707 | public Video[] videos = null;
708 |
709 | /**
710 | * Provides A+ Content of this product.
711 | *
712 | * The {@code aplus} parameter is mandatory for access. To request live
713 | * data for this field, the {@code offers} parameter must also be included. Returns {@code null}
714 | * if unavailable.
715 | */
716 | public APlus[] aPlus = null;
717 |
718 |
719 | /**
720 | * Specific uses for the product, providing detailed applications.
721 | * Example: {"Relaxation", "Decoration"}
722 | */
723 | public String[] specificUsesForProduct = null;
724 |
725 | /**
726 | * A categorization name of products that behave similarly,
influencing how sales rank is calculated and displayed,
especially for product variations.
727 | * Example: {"apparel", "kitchen"}
728 | */
729 | public String websiteDisplayGroupName = null;
730 |
731 | /**
732 | * A categorization of products that behave similarly,
influencing how sales rank is calculated and displayed,
especially for product variations.
733 | * Example: {"apparel_display_on_website", "kitchen_display_on_website"}
734 | */
735 | public String websiteDisplayGroup = null;
736 |
737 | /**
738 | * For books only: An array listing other available formats or bindings of a book, excluding the current format.
739 | */
740 | public Format[] formats = null;
741 |
742 | /**
743 | * The highest business discount percentage, if available.
744 | * Example: 14
745 | */
746 | public Short businessDiscount = null;
747 |
748 | /**
749 | * KeepaTime timestamp of the last business discount percentage update.
750 | */
751 | public Integer lastBusinessDiscountUpdate = null;
752 |
753 | /**
754 | * Safety warnings associated with the product to inform users of potential hazards.
755 | * Example: "Keep away from open flames."
756 | */
757 | public String safetyWarning = null;
758 |
759 | /**
760 | * Benefits of using the product, highlighting its advantages.
761 | * Example: "Promotes relaxation and stress relief."
762 | */
763 | public String productBenefit = null;
764 |
765 | /**
766 | * Indicates whether batteries are required for the product to function.
767 | * Example: true or false
768 | */
769 | public Boolean batteriesRequired = null;
770 |
771 | /**
772 | * Indicates whether batteries are included with the product upon purchase.
773 | * Example: true or false
774 | */
775 | public Boolean batteriesIncluded = null;
776 |
777 | /**
778 | * Keywords describing the target audience for the product.
779 | * Example: "Adults, Gift for Her"
780 | */
781 | public String targetAudienceKeyword = null;
782 |
783 | /**
784 | * The style of the product, which may influence its aesthetic appeal.
785 | * Example: "Modern", "Vintage"
786 | */
787 | public String style = null;
788 |
789 | /**
790 | * Components included with the product, detailing what is provided upon purchase.
791 | * Example: "Candle, Wick, Box"
792 | */
793 | public String includedComponents = null;
794 |
795 | /**
796 | * Material of the product, specifying the primary substances used in its construction.
797 | * Example: "Soy Wax, Cotton"
798 | */
799 | public String material = null;
800 |
801 |
802 | /**
803 | * Integer[][] - two dimensional price history array.
804 | * First dimension: {@link Product.CsvType}
805 | * Second dimension:
806 | * Each array has the format timestamp, price, […]. To get an uncompressed timestamp use {@link KeepaTime#keepaMinuteToUnixInMillis(int)}.
807 | * Example: "csv[0]": [411180,4900, ... ]
808 | * timestamp: 411180 => 1318510800000
809 | * price: 4900 => $ 49.00 (if domainId is 5, Japan, then price: 4900 => ¥ 4900)
810 | * A price of '-1' means that there was no offer at the given timestamp (e.g. out of stock).
811 | */
812 | public int[][] csv = null;
813 |
814 | public enum CsvType {
815 | /**
816 | * Amazon price history
817 | */
818 | AMAZON(0, true, true, false, false),
819 |
820 | /**
821 | * Marketplace/3rd party New price history - Amazon is considered to be part of the marketplace as well,
822 | * so if Amazon has the overall lowest new (!) price, the marketplace new price in the corresponding time interval
823 | * will be identical to the Amazon price (except if there is only one marketplace offer).
824 | * Shipping and Handling costs not included!
825 | */
826 | NEW(1, true, true, false, false),
827 |
828 | /**
829 | * Marketplace/3rd party Used price history
830 | */
831 | USED(2, true, true, false, false),
832 |
833 | /**
834 | * Sales Rank history. Not every product has a Sales Rank.
835 | */
836 | SALES(3, false, true, false, false),
837 |
838 | /**
839 | * List Price history
840 | */
841 | LISTPRICE(4, true, false, false, false),
842 |
843 | /**
844 | * Collectible Price history
845 | */
846 | COLLECTIBLE(5, true, true, false, false),
847 |
848 | /**
849 | * Refurbished Price history
850 | */
851 | REFURBISHED(6, true, true, false, false),
852 |
853 | /**
854 | * 3rd party (not including Amazon) New price history including shipping costs, only fulfilled by merchant (FBM).
855 | */
856 | NEW_FBM_SHIPPING(7, true, true, true, true),
857 |
858 | /**
859 | * 3rd party (not including Amazon) New price history including shipping costs, only fulfilled by merchant (FBM).
860 | */
861 | LIGHTNING_DEAL(8, true, true, false, false),
862 |
863 | /**
864 | * Amazon Warehouse Deals price history. Mostly of used condition, rarely new.
865 | */
866 | WAREHOUSE(9, true, true, false, true),
867 |
868 | /**
869 | * Price history of the lowest 3rd party (not including Amazon/Warehouse) New offer that is fulfilled by Amazon
870 | */
871 | NEW_FBA(10, true, true, false, true),
872 |
873 | /**
874 | * New offer count history
875 | */
876 | COUNT_NEW(11, false, false, false, false),
877 |
878 | /**
879 | * Used offer count history
880 | */
881 | COUNT_USED(12, false, false, false, false),
882 |
883 | /**
884 | * Refurbished offer count history
885 | */
886 | COUNT_REFURBISHED(13, false, false, false, false),
887 |
888 | /**
889 | * Collectible offer count history
890 | */
891 | COUNT_COLLECTIBLE(14, false, false, false, false),
892 |
893 | /**
894 | * History of past updates to all offers-parameter related data: offers, buyBoxSellerIdHistory, isSNS, isRedirectASIN and the csv types
895 | * NEW_SHIPPING, WAREHOUSE, FBA, BUY_BOX_SHIPPING, USED_*_SHIPPING, COLLECTIBLE_*_SHIPPING and REFURBISHED_SHIPPING.
896 | * As updates to those fields are infrequent it is important to know when our system updated them.
897 | * The absolute value indicates the amount of offers fetched at the given time.
898 | * If the value is positive it means all available offers were fetched. It's negative if there were more offers than fetched.
899 | */
900 | EXTRA_INFO_UPDATES(15, false, false, false, true),
901 |
902 | /**
903 | * The product's rating history. A rating is an integer from 0 to 50 (e.g. 45 = 4.5 stars)
904 | */
905 | RATING(16, false, false, false, true),
906 | /**
907 | * The product's review count history.
908 | */
909 | COUNT_REVIEWS(17, false, false, false, true),
910 |
911 | /**
912 | * The price history of the buy box. If no offer qualified for the buy box the price has the value -1. Including shipping costs.
913 | */
914 | BUY_BOX_SHIPPING(18, true, false, true, true),
915 |
916 | /**
917 | * "Used - Like New" price history including shipping costs.
918 | */
919 | USED_NEW_SHIPPING(19, true, true, true, true),
920 |
921 | /**
922 | * "Used - Very Good" price history including shipping costs.
923 | */
924 | USED_VERY_GOOD_SHIPPING(20, true, true, true, true),
925 |
926 | /**
927 | * "Used - Good" price history including shipping costs.
928 | */
929 | USED_GOOD_SHIPPING(21, true, true, true, true),
930 |
931 | /**
932 | * "Used - Acceptable" price history including shipping costs.
933 | */
934 | USED_ACCEPTABLE_SHIPPING(22, true, true, true, true),
935 |
936 | /**
937 | * "Collectible - Like New" price history including shipping costs.
938 | */
939 | COLLECTIBLE_NEW_SHIPPING(23, true, true, true, true),
940 |
941 | /**
942 | * "Collectible - Very Good" price history including shipping costs.
943 | */
944 | COLLECTIBLE_VERY_GOOD_SHIPPING(24, true, true, true, true),
945 |
946 | /**
947 | * "Collectible - Good" price history including shipping costs.
948 | */
949 | COLLECTIBLE_GOOD_SHIPPING(25, true, true, true, true),
950 |
951 | /**
952 | * "Collectible - Acceptable" price history including shipping costs.
953 | */
954 | COLLECTIBLE_ACCEPTABLE_SHIPPING(26, true, true, true, true),
955 |
956 | /**
957 | * Refurbished price history including shipping costs.
958 | */
959 | REFURBISHED_SHIPPING(27, true, true, true, true),
960 |
961 | /**
962 | * Price history of the lowest new price on the respective eBay locale, including shipping costs.
963 | */
964 | EBAY_NEW_SHIPPING(28, true, false, true, false),
965 |
966 | /**
967 | * Price history of the lowest used price on the respective eBay locale, including shipping costs.
968 | */
969 | EBAY_USED_SHIPPING(29, true, false, true, false),
970 |
971 | /**
972 | * The trade in price history. Amazon trade-in is not available for every locale.
973 | */
974 | TRADE_IN(30, true, false, false, false),
975 |
976 | /**
977 | * Rental price history. Requires use of the rental and offers parameter. Amazon Rental is only available for Amazon US.
978 | */
979 | RENT(31, true, false, false, true),
980 |
981 |
982 | /**
983 | * The price history of the Used buy box (any sub-condition). If no offer qualified for the used buy box the price has the value -1. Including shipping costs.
984 | */
985 | BUY_BOX_USED_SHIPPING(32, true, true, true, true),
986 |
987 | /**
988 | * Price history of the lowest Prime exclusive New offer.
989 | */
990 | PRIME_EXCL(33, true, true, false, true);
991 |
992 | public final int index;
993 |
994 | /**
995 | * If the values are prices.
996 | */
997 | public final boolean isPrice;
998 |
999 | /**
1000 | * If the CSV contains shipping costs
1001 | * If true, csv format has 3 entries: time, price, shippingCosts
1002 | */
1003 | public final boolean isWithShipping;
1004 |
1005 | /**
1006 | * If the type can be used to request deals.
1007 | */
1008 | public final boolean isDealRelevant;
1009 |
1010 | /**
1011 | * True if the data is only accessible in conjunction with the 'offers' parameter of the product request.
1012 | */
1013 | public final boolean isExtraData;
1014 |
1015 | public static final CsvType[] values = CsvType.values();
1016 |
1017 | CsvType(int i, boolean price, boolean deal, boolean shipping, boolean extra) {
1018 | isPrice = price;
1019 | isDealRelevant = deal;
1020 | isExtraData = extra;
1021 | isWithShipping = shipping;
1022 | index = i;
1023 | }
1024 |
1025 | public static CsvType getCSVTypeFromIndex(int index) {
1026 | for (CsvType type : CsvType.values) {
1027 | if (type.index == index) return type;
1028 | }
1029 | return AMAZON;
1030 | }
1031 | }
1032 |
1033 | public enum AvailabilityType {
1034 | /**
1035 | * No Amazon offer exists
1036 | */
1037 | NO_OFFER(-1),
1038 |
1039 | /**
1040 | * Amazon offer is in stock and shippable
1041 | */
1042 | NOW(0),
1043 |
1044 | /**
1045 | * Amazon offer is currently not in stock but will be in the future - pre-order
1046 | */
1047 | PREORDERABLE(1),
1048 |
1049 | /**
1050 | * Amazon offer availability is "unknown"
1051 | */
1052 | UNKNOWN(2),
1053 |
1054 | /**
1055 | * Amazon offer is currently not in stock but will be in the future - back-order
1056 | */
1057 | BACKORDERABLE(3),
1058 |
1059 | /**
1060 | * Amazon offer availability is delay. Check availabilityAmazonDelay field for details.
1061 | */
1062 | DELAYED(4);
1063 |
1064 | public int code;
1065 |
1066 | AvailabilityType(int i) {
1067 | code = i;
1068 | }
1069 |
1070 | private static final AvailabilityType[] values = AvailabilityType.values();
1071 |
1072 | public static AvailabilityType fromValue(int value) throws IllegalArgumentException {
1073 | try {
1074 | return AvailabilityType.values[value];
1075 | } catch (ArrayIndexOutOfBoundsException e) {
1076 | throw new IllegalArgumentException("Unknown AvailabilityType value: " + value);
1077 | }
1078 | }
1079 | }
1080 |
1081 | public enum ProductType {
1082 | /**
1083 | * standard product - everything accessible
1084 | */
1085 | STANDARD((byte) 0),
1086 |
1087 | /**
1088 | * downloadable product – no marketplace price data
1089 | */
1090 | DOWNLOADABLE((byte) 1),
1091 |
1092 | /**
1093 | * ebook – no price data and sales rank accessible
1094 | */
1095 | EBOOK((byte) 2),
1096 |
1097 | /**
1098 | * no data accessible (hidden prices due to MAP - minimum advertised price)
1099 | */
1100 | UNACCESSIBLE((byte) 3),
1101 |
1102 | /**
1103 | * no data available due to invalid or deprecated asin, or other issues
1104 | */
1105 | INVALID((byte) 4),
1106 |
1107 | /**
1108 | * Product is a parent ASIN. No product data accessible, variationCSV is set
1109 | */
1110 | VARIATION_PARENT((byte) 5);
1111 |
1112 | public final byte code;
1113 |
1114 | ProductType(byte code) {
1115 | this.code = code;
1116 | }
1117 |
1118 | public static final ProductType[] values = ProductType.values();
1119 |
1120 | public static ProductType fromValue(int value) throws IllegalArgumentException {
1121 | try {
1122 | return ProductType.values[value];
1123 | } catch (ArrayIndexOutOfBoundsException e) {
1124 | throw new IllegalArgumentException("Unknown enum value: " + value);
1125 | }
1126 | }
1127 |
1128 | @Override
1129 | public String toString() {
1130 | return String.valueOf(code);
1131 | }
1132 | }
1133 |
1134 |
1135 | public enum VideoCreatorType {
1136 | Main,
1137 | Customer,
1138 | Seller,
1139 | Influencer,
1140 | Vendor,
1141 | ThirdParty,
1142 | Amazon,
1143 | Merchant,
1144 | Brand
1145 | }
1146 |
1147 | public class CategoryTreeEntry {
1148 | public long catId;
1149 | public String name;
1150 | }
1151 |
1152 | public class HazardousMaterial {
1153 | public String aspect;
1154 | public String value;
1155 | }
1156 |
1157 |
1158 | public static class RentalObject {
1159 | public int initialPrice = -1;
1160 | public int shortExtnPrice = -1;
1161 | public int longExtnPrice = -1;
1162 | public int fullPrice = -1;
1163 | }
1164 |
1165 | public static class PromotionObject {
1166 | /**
1167 | * The type of promotion
1168 | **/
1169 | public PromotionType type = null;
1170 | public int amount = -1;
1171 | public int discountPercent = -1;
1172 | public Integer snsBulkDiscountPercent = null;
1173 | }
1174 |
1175 | public static class UnitCountObject {
1176 | public Double unitValue;
1177 | public String unitType;
1178 | public Double eachUnitCount;
1179 | }
1180 |
1181 | public static class Video {
1182 | public String title;
1183 |
1184 | /**
1185 | * Full Amazon image path:
1186 | * https://m.media-amazon.com/images/I/_image_
1187 | */
1188 | public String image;
1189 |
1190 | /**
1191 | * in seconds
1192 | */
1193 | public short duration;
1194 | public VideoCreatorType creator;
1195 | public String name;
1196 | public String url;
1197 | }
1198 |
1199 | public static class APlus {
1200 | public APlusModule[] module;
1201 | public boolean fromManufacturer;
1202 | }
1203 |
1204 | public static class Image {
1205 |
1206 | /**
1207 | * The filename of the large image.
1208 | */
1209 | public String l;
1210 |
1211 | /**
1212 | * The height of the large image in pixels.
1213 | */
1214 | public Short lH;
1215 |
1216 | /**
1217 | * The width of the large image in pixels.
1218 | */
1219 | public Short lW;
1220 |
1221 | /**
1222 | * The filename of the medium image.
1223 | */
1224 | public String m;
1225 |
1226 | /**
1227 | * The height of the medium image in pixels.
1228 | */
1229 | public Short mH;
1230 |
1231 | /**
1232 | * The width of the medium image in pixels.
1233 | */
1234 | public Short mW;
1235 | }
1236 |
1237 | public static class Format {
1238 | /**
1239 | * The ASIN of the format.
1240 | */
1241 | public String asin = null;
1242 |
1243 | /**
1244 | * The type of format, which can be one of the following: Kindle, Paperback, Hardcover, Audiobook, or Spiral-bound.
1245 | */
1246 | public String format = null;
1247 | }
1248 |
1249 | public static class APlusModule {
1250 | public String[] text;
1251 | public String[] image;
1252 | public String[] video;
1253 | }
1254 |
1255 | public static class VariationObject {
1256 | /**
1257 | * Variation ASIN
1258 | **/
1259 | public String asin = null;
1260 | /**
1261 | * This variation ASIN's dimension attributes
1262 | **/
1263 | public VariationAttributeObject[] attributes = null;
1264 | }
1265 |
1266 | public static class VariationAttributeObject {
1267 | /**
1268 | * dimension type, e.g. Color
1269 | **/
1270 | public String dimension = null;
1271 | /**
1272 | * dimension value, e.g. Red
1273 | **/
1274 | public String value = null;
1275 | }
1276 |
1277 | /**
1278 | * Contains detailed FBA fees. If the total fee is 0 the product does not have (valid) dimensions and thus the fee can not be calculated.
1279 | */
1280 | public static class FBAFeesObject {
1281 | public int storageFee;
1282 | public int storageFeeTax;
1283 | public int pickAndPackFee;
1284 | public int pickAndPackFeeTax;
1285 | }
1286 |
1287 | public enum PromotionType {
1288 | SNS("SNS"),
1289 | PrimeExclusive("PrimeExclusive");
1290 |
1291 | public final String type;
1292 |
1293 | PromotionType(String name) {
1294 | type = name;
1295 | }
1296 |
1297 | public String toString() {
1298 | return type;
1299 | }
1300 | }
1301 |
1302 | @Override
1303 | public String toString() {
1304 | return gson.toJson(this);
1305 | }
1306 |
1307 |
1308 | }
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/structs/RequestError.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 | import static com.keepa.api.backend.helper.Utility.gsonPretty;
4 |
5 | /**
6 | * Contains information about an API error.
7 | */
8 | public class RequestError {
9 | String type, message, details;
10 |
11 | @Override
12 | public String toString() {
13 | return gsonPretty.toJson(this);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/keepa/api/backend/structs/Response.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 | import com.keepa.api.backend.KeepaAPI;
4 |
5 | import java.util.HashMap;
6 |
7 | import static com.keepa.api.backend.helper.Utility.gson;
8 | import static com.keepa.api.backend.helper.Utility.gsonPretty;
9 |
10 | /**
11 | * Common Keepa API Response
12 | */
13 | public class Response {
14 | /**
15 | * Server time when response was sent.
16 | */
17 | public long timestamp = System.currentTimeMillis();
18 |
19 | /**
20 | * States how many ASINs may be requested before the assigned API contingent is depleted.
21 | * If the contigent is depleted, HTTP status code 503 will be delivered with the message:
22 | * "You are submitting requests too quickly and your requests are being throttled."
23 | */
24 | public int tokensLeft = 0;
25 |
26 | /**
27 | * Milliseconds till new tokens are generated. Use this if your contigent is depleted to wait before you try a new request. Tokens are generated every 5 minutes.
28 | */
29 | public int refillIn = 0;
30 |
31 | /**
32 | * Token refill rate per minute.
33 | */
34 | public int refillRate = 0;
35 |
36 | /**
37 | * total time the request took (local, including latencies and connection establishment), in milliseconds
38 | */
39 | public long requestTime = 0;
40 |
41 | /**
42 | * time the request's processing took (remote), in milliseconds
43 | */
44 | public int processingTimeInMs = 0;
45 |
46 | /**
47 | * Token flow reduction
48 | */
49 | public double tokenFlowReduction = 0;
50 |
51 | /**
52 | * Tokens used for call
53 | */
54 | public int tokensConsumed = 0;
55 |
56 | /**
57 | * Status of the request.
58 | */
59 | public KeepaAPI.ResponseStatus status = KeepaAPI.ResponseStatus.PENDING;
60 |
61 | /**
62 | * Results of the product request
63 | */
64 | public Product[] products = null;
65 |
66 | /**
67 | * Results of the category lookup and search
68 | */
69 | public HashMap categories = null;
70 |
71 | /**
72 | * Results of the category lookup and search includeParents parameter
73 | */
74 | public HashMap categoryParents = null;
75 |
76 | /**
77 | * Results of the deals request
78 | */
79 | public DealResponse deals = null;
80 |
81 | /**
82 | * Results of the best sellers request
83 | */
84 | public BestSellers bestSellersList = null;
85 |
86 | /**
87 | * Results of the deals request
88 | */
89 | public HashMap sellers = null;
90 |
91 | /**
92 | * Results of get and add tracking operations
93 | */
94 | public Tracking[] trackings = null;
95 |
96 | /**
97 | * Results of get and add tracking operations
98 | */
99 | public Notification[] notifications = null;
100 |
101 | /**
102 | * A list of ASINs. Result of, but not limited to, the get tracking list operation
103 | */
104 | public String[] asinList = null;
105 |
106 | /**
107 | * Estimated count of all matched products.
108 | */
109 | public Integer totalResults = null;
110 |
111 | /**
112 | * A list of sellerIds.
113 | */
114 | public String[] sellerIdList = null;
115 |
116 | /**
117 | * A list of lightning deals.
118 | */
119 | public LightningDeal[] lightningDeals = null;
120 |
121 | /**
122 | * Contains information about any error that might have occurred.
123 | */
124 | public RequestError error = null;
125 |
126 | /**
127 | * Contains request specific additional output.
128 | */
129 | public String additional = null;
130 |
131 | /**
132 | * If the reqeust failed due to an Java exception (e.g. network error), this contains the exception object.
133 | */
134 | public transient Exception exception;
135 |
136 | @Override
137 | public String toString() {
138 | if(status == KeepaAPI.ResponseStatus.OK)
139 | return gson.toJson(this);
140 | else
141 | return gsonPretty.toJson(this);
142 | }
143 |
144 | }
145 |
146 |
147 |
--------------------------------------------------------------------------------
/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 | /**
125 | * The business address. Each entry of the array contains one address line.
126 | * The last entry contains the 2 letter country code. null if not available.
127 | * Example: [123 Main Street, New York, NY, 10001, US]
128 | */
129 | public String[] address;
130 |
131 | /**
132 | * Contains up to 5 of the most recent customer feedbacks.
133 | * Each feedback object in the array contains the following fields
134 | */
135 | public FeedbackObject[] recentFeedback;
136 |
137 | /**
138 | * States the time of our last rating data update of this seller, in Keepa Time minutes.
139 | * Use {@link KeepaTime#keepaMinuteToUnixInMillis(int)} (long)} to get an uncompressed timestamp (Unix epoch time).
140 | */
141 | public int lastRatingUpdate;
142 |
143 | /**
144 | * Contains the neutral percentage ratings for the last 30 days, 90 days, 365 days and lifetime, in that order.
145 | * A neutral rating is a 3 star rating.
146 | * Example: [1, 1, 1, 2]
147 | */
148 | public int[] neutralRating = null;
149 |
150 | /**
151 | * Contains the negative percentage ratings for the last 30 days, 90 days, 365 days and lifetime, in that order.
152 | * A negative rating is a 1 or 2 star rating.
153 | * Example: [3, 1, 1, 3]
154 | */
155 | public int[] negativeRating = null;
156 |
157 | /**
158 | * Contains the positive percentage ratings for the last 30 days, 90 days, 365 days and lifetime, in that order.
159 | * A positive rating is a 4 or 5 star rating.
160 | * Example: [96, 98, 98, 95]
161 | */
162 | public int[] positiveRating = null;
163 |
164 | /**
165 | * Contains the rating counts for the last 30 days, 90 days, 365 days and lifetime, in that order.
166 | * Example: [3, 10, 98, 321]
167 | */
168 | public int[] ratingCount = null;
169 |
170 | /**
171 | * The customer services address. Each entry of the array contains one address line.
172 | * The last entry contains the 2 letter country code. null if not available.
173 | * Example: [123 Main Street, New York, NY, 10001, US]
174 | */
175 | public String[] customerServicesAddress;
176 |
177 | /**
178 | * The Trade Register Number. null if not available.
179 | * Example: HRB 123 456
180 | */
181 | public String tradeNumber;
182 |
183 | /**
184 | * The business name. null if not available.
185 | * Example: Keepa GmbH
186 | */
187 | public String businessName;
188 |
189 | /**
190 | * The VAT number. null if not available.
191 | * Example: DE123456789
192 | */
193 | public String vatID;
194 |
195 | /**
196 | * The phone number. null if not available.
197 | * Example: 800 1234 567
198 | */
199 | public String phoneNumber;
200 |
201 | /**
202 | * The business type. null if not available.
203 | * Example: Unternehmen in Privatbesitz
204 | */
205 | public String businessType;
206 |
207 | /**
208 | * The share capital. null if not available.
209 | * Example: 25000
210 | */
211 | public String shareCapital;
212 |
213 | /**
214 | * The name of the business representative. null if not available.
215 | * Example: Max Mustermann
216 | */
217 | public String representative;
218 |
219 | /**
220 | * The email address of the business. null if not available.
221 | * Example: info@keepa.com
222 | */
223 | public String email;
224 |
225 | public int currentRating;
226 | public int currentRatingCount;
227 | public int ratingsLast30Days;
228 |
229 | public enum MerchantCsvType {
230 | RATING(0, false),
231 | RATING_COUNT(1, false);
232 |
233 | /**
234 | * If the values are prices.
235 | */
236 | public final boolean isPrice;
237 |
238 | public final int index;
239 | public static final MerchantCsvType[] values = MerchantCsvType.values();
240 |
241 | MerchantCsvType(int i, boolean price) {
242 | index = i;
243 | isPrice = price;
244 | }
245 |
246 | public static MerchantCsvType getCSVTypeFromIndex(int index) {
247 | for (MerchantCsvType type : MerchantCsvType.values) {
248 | if (type.index == index) return type;
249 | }
250 | return RATING;
251 | }
252 | }
253 |
254 | public static class MerchantBrandStatistics {
255 |
256 | /**
257 | * the brand (in all lower-case)
258 | */
259 | public String brand;
260 |
261 | /**
262 | * the number of products this merchant sells with this brand
263 | */
264 | public int productCount;
265 |
266 | /**
267 | * the 30 day average sales rank of these products
268 | */
269 | public int avg30SalesRank;
270 |
271 | /**
272 | * how many of these products have an Amazon offer
273 | */
274 | public int productCountWithAmazonOffer;
275 | }
276 |
277 | public static class MerchantCategoryStatistics {
278 |
279 | /**
280 | * the category id
281 | */
282 | public long catId;
283 |
284 | /**
285 | * the number of products this merchant sells with this category
286 | */
287 | public int productCount;
288 |
289 | /**
290 | * the 30 day average sales rank of these products
291 | */
292 | public int avg30SalesRank;
293 |
294 | /**
295 | * how many of these products have an Amazon offer
296 | */
297 | public int productCountWithAmazonOffer;
298 | }
299 |
300 | public static class FeedbackObject {
301 | /**
302 | * the feedback star rating - value range from 10 (1 star) to 50 (5 stars)
303 | */
304 | public int rating;
305 |
306 | /**
307 | * timestamp of the feedback, in Keepa Time minutes
308 | */
309 | public int date;
310 |
311 | /**
312 | * the feedback text
313 | */
314 | public String feedback = null;
315 |
316 | /**
317 | * whether or not the feedback is striked
318 | */
319 | public boolean isStriked;
320 | }
321 |
322 | @Override
323 | public String toString() {
324 | return gson.toJson(this);
325 | }
326 | }
327 |
--------------------------------------------------------------------------------
/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 | * Only set when the offers parameter was used. Price of the used buy box, if existent. Otherwise "-1" or null
313 | */
314 | public Integer buyBoxUsedPrice = null;
315 |
316 | /**
317 | * Only set when the offers parameter was used. Shipping cost of the used buy box, if existent. Otherwise "-1" or null
318 | */
319 | public Integer buyBoxUsedShipping = null;
320 |
321 | /**
322 | * Only set when the offers parameter was used. Seller id of the used boy box, if existent. Otherwise null.
323 | */
324 | public String buyBoxUsedSellerId = null;
325 |
326 | /**
327 | * Only set when the offers parameter was used. Whether or not the used buy box is fulfilled by Amazon.
328 | */
329 | public Boolean buyBoxUsedIsFBA = null;
330 |
331 | /**
332 | * Only set when the offers parameter was used. The used sub type condition of the used buy box offer
333 | *
The {@link Offer.OfferCondition} condition of the offered product. Integer value:
334 | *
2 - Used - Like New
335 | *
3 - Used - Very Good
336 | *
4 - Used - Good
337 | *
5 - Used - Acceptable
338 | *
Note: Open Box conditions will be coded as Used conditions.
339 | */
340 | public Byte buyBoxUsedCondition = null;
341 |
342 | /**
343 | * 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.
344 | */
345 | public LinkedHashMap buyBoxUsedStats = null;
346 |
347 | /**
348 | * 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).
349 | */
350 | public Boolean isAddonItem = null;
351 |
352 | /**
353 | * 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.
354 | */
355 | public String[] sellerIdsLowestFBA = null;
356 |
357 | /**
358 | * 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.
359 | */
360 | public String[] sellerIdsLowestFBM = null;
361 |
362 | /**
363 | * Only set when the offers parameter was used. Count of retrieved live FBA offers.
364 | */
365 | public int offerCountFBA = -2;
366 |
367 | /**
368 | * Only set when the offers parameter was used. Count of retrieved live FBM offers.
369 | */
370 | public int offerCountFBM = -2;
371 |
372 | /**
373 | * The count of sales rank drops (from high value to low value) within the last 30 days which are considered to indicate sales.
374 | */
375 | public int salesRankDrops30 = -1;
376 |
377 | /**
378 | * The count of sales rank drops (from high value to low value) within the last 90 days which are considered to indicate sales.
379 | */
380 | public int salesRankDrops90 = -1;
381 |
382 | /**
383 | * The count of sales rank drops (from high value to low value) within the last 180 days which are considered to indicate sales.
384 | */
385 | public int salesRankDrops180 = -1;
386 |
387 | /**
388 | * The count of sales rank drops (from high value to low value) within the last 365 days which are considered to indicate sales.
389 | */
390 | public int salesRankDrops365 = -1;
391 |
392 | public static class BuyBoxStatsObject {
393 | /** an approximation of the percentage the seller won the buy box **/
394 | public float percentageWon;
395 | /** avg. price of the Buy Box offer of this seller **/
396 | public int avgPrice;
397 | /** avg. "New" offer count during the time the seller held the Buy Box **/
398 | public int avgNewOfferCount;
399 | /** whether or not this offer is fulfilled by Amazon **/
400 | public boolean isFBA;
401 | /** last time the seller won the buy box **/
402 | public int lastSeen;
403 | }
404 |
405 | @Override
406 | public String toString() {
407 | return gson.toJson(this);
408 | }
409 | }
410 |
--------------------------------------------------------------------------------
/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/TrackingRequest.java:
--------------------------------------------------------------------------------
1 | package com.keepa.api.backend.structs;
2 |
3 | /**
4 | * Required by the Add Tracking request.
5 | */
6 | public class TrackingRequest {
7 |
8 | public TrackingRequest(String asin, AmazonLocale mainDomainId, int updateInterval){
9 | this.asin = asin;
10 | this.mainDomainId = (byte) mainDomainId.ordinal();
11 | this.updateInterval = updateInterval < 1 ? 1 : updateInterval;
12 | }
13 |
14 | /**
15 | * The product ASIN to track
16 | */
17 | public String asin;
18 |
19 | /**
20 | * The time to live in hours until the tracking expires and is deleted.
21 | * When setting the value through the _Add Tracking_ request it is in relation to the time of request. Possible values:
22 | * any positive integer: time to live in hours
23 | * 0: never expires
24 | * any negative integer:
25 | * tracking already exists: keep the original `ttl`
tracking is new: use the absolute value as `ttl`
26 | *
27 | */
28 | public int ttl = 24 * 365 * 2;
29 |
30 | /**
31 | * Trigger a notification if tracking expires or is removed by the system (e.g. product deprecated)
32 | */
33 | public boolean expireNotify = false;
34 |
35 | /**
36 | * Whether or not all desired prices are in the currency of the mainDomainId. If false they will be converted.
37 | */
38 | public boolean desiredPricesInMainCurrency = true;
39 |
40 | /**
41 | * The main Amazon locale of this tracking determines the currency used for all desired prices.
42 | * Integer value for the Amazon locale {@link AmazonLocale}
43 | */
44 | public byte mainDomainId;
45 |
46 | /**
47 | * Contains all settings for price or value related tracking criteria
48 | */
49 | public Tracking.TrackingThresholdValue[] thresholdValues = null;
50 |
51 | /**
52 | * Contains specific, meta tracking criteria, like out of stock.
53 | */
54 | public Tracking.TrackingNotifyIf[] notifyIf = null;
55 |
56 | /**
57 | * Determines through which channels we will send notifications.
58 | * Uses NotificationType indexing {@link Tracking.NotificationType}. True means the channel will be used.
59 | */
60 | public boolean[] notificationType = null;
61 |
62 | /**
63 | * A tracking specific rearm timer.
64 | * -1 = use default notification timer of the user account (changeable via the website settings)
65 | * 0 = never notify a desired price more than once
66 | * larger than 0 = rearm the desired price after x minutes.
67 | */
68 | public int individualNotificationInterval = -1;
69 |
70 | /**
71 | * The update interval, in hours. Determines how often our system will trigger a product update. A setting of 1
72 | * hour will not trigger an update exactly every 60 minutes, but as close to that as it is efficient for our system.
73 | * Throughout a day it will be updated 24 times, but the updates are not perfectly distributed.
74 | * Possible values: Any integer between 0 and 25. Default is 1.
75 | */
76 | public int updateInterval = 1;
77 |
78 | /**
79 | * Meta data of this tracking (max length is 500 characters). You can use this to store any string with this tracking.
80 | */
81 | public String metaData = null;
82 | }
83 |
--------------------------------------------------------------------------------