shopsavvy-sdk 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4faa444cc609f529b67d86aa8bd5c14dd8e8119888039a9baffa9801105e178e
4
- data.tar.gz: 5b61ec2bbc289153cffaef95cd36af9b3dfceb512f18b0a7f0a66222d2ccc4a4
3
+ metadata.gz: 6179b544c407fb648b93d79b8a0b62e2aacb3bbfbebd7f3b741887065f06cd03
4
+ data.tar.gz: 8802bfeb393b61ad00d75e139649150cf1b7ed9be50b36b443dd3951c97fd11d
5
5
  SHA512:
6
- metadata.gz: 46aba76fa5844ac2d89217e2c6d647f2fcd75b5497b442d776b4f23efab1fab48dfdfbaf3e6a295abf93c333dd5856195947948efd8b762d100d1e95e161caa3
7
- data.tar.gz: 3ed236f6d857dfe8600b5d3d5ee40e8bb8c6c16066f01df31b8a0f724fabbd3780dd24edfc0150a75c7ba7b03751eaecaf13664a7fc2453f41932a9c8f730f98
6
+ metadata.gz: 92ebba528d0ba098c7fa731b8fa854cb9945503eb2e9f096d5258c4be33de8dee334d52be21d04a4ba84a06ecf144e6acc05557323a49a91dcff087f7ab3b8aa
7
+ data.tar.gz: 7c6104eb59e8112921087076bf4794d63e8fc3d0d7ccb29be61c26e53bd2be20d4735aa461693f31fb4016f57445859a35a9e2cf3cbe5c9683276dabdab5439c
data/README.md CHANGED
@@ -1,172 +1,316 @@
1
1
  # ShopSavvy Data API - Ruby SDK
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/shopsavvy_data_api.svg)](https://badge.fury.io/rb/shopsavvy_data_api)
4
- [![Ruby](https://img.shields.io/badge/Ruby-%3E%3D%202.7.0-red.svg)](https://www.ruby-lang.org/)
3
+ [![Gem Version](https://badge.fury.io/rb/shopsavvy-sdk.svg)](https://badge.fury.io/rb/shopsavvy-sdk)
4
+ [![Ruby](https://img.shields.io/badge/Ruby-%3E%3D%202.6.0-red.svg)](https://www.ruby-lang.org/)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![Documentation](https://img.shields.io/badge/docs-shopsavvy.com-blue)](https://shopsavvy.com/data/documentation)
6
7
 
7
- Official Ruby SDK for the [ShopSavvy Data API](https://shopsavvy.com/data). Access product data, pricing information, and price history across thousands of retailers and millions of products.
8
+ Official Ruby SDK for the [ShopSavvy Data API](https://shopsavvy.com/data). Access comprehensive product data, real-time pricing, and historical price trends across **thousands of retailers** and **millions of products**.
8
9
 
9
- ## šŸš€ Quick Start
10
-
11
- ### Installation
12
-
13
- Add this line to your application's Gemfile:
10
+ ## ⚔ 30-Second Quick Start
14
11
 
15
12
  ```ruby
16
- gem 'shopsavvy_data_api'
13
+ # Install
14
+ gem install shopsavvy-sdk
15
+
16
+ # Use
17
+ require 'shopsavvy_data_api'
18
+ client = ShopsavvyDataApi.new(api_key: 'ss_live_your_api_key_here')
19
+ product = client.get_product_details('012345678901')
20
+ puts "#{product.data.name} - Best price: $#{client.get_current_offers('012345678901').data.min_by(&:price).price}"
17
21
  ```
18
22
 
19
- And then execute:
23
+ ## šŸ“Š Feature Comparison
20
24
 
21
- ```bash
22
- bundle install
23
- ```
25
+ | Feature | Free Tier | Pro | Enterprise |
26
+ |---------|-----------|-----|-----------|
27
+ | **API Calls/Month** | 1,000 | 100,000 | Unlimited |
28
+ | **Product Details** | āœ… | āœ… | āœ… |
29
+ | **Real-time Pricing** | āœ… | āœ… | āœ… |
30
+ | **Price History** | 30 days | 1 year | 5+ years |
31
+ | **Bulk Operations** | 10/batch | 100/batch | 1000/batch |
32
+ | **Retailer Coverage** | 50+ | 500+ | 1000+ |
33
+ | **Rate Limiting** | 60/hour | 1000/hour | Custom |
34
+ | **Support** | Community | Email | Phone + Dedicated |
24
35
 
25
- Or install it yourself as:
36
+ ## šŸš€ Installation & Setup
26
37
 
38
+ ### Installation
39
+
40
+ Add to your Gemfile:
41
+ ```ruby
42
+ gem 'shopsavvy-sdk'
43
+ ```
44
+
45
+ Or install directly:
27
46
  ```bash
28
- gem install shopsavvy_data_api
47
+ gem install shopsavvy-sdk
29
48
  ```
30
49
 
31
50
  ### Get Your API Key
32
51
 
33
- 1. Visit [shopsavvy.com/data](https://shopsavvy.com/data)
34
- 2. Sign up for an account
35
- 3. Choose a subscription plan
36
- 4. Get your API key from the dashboard
52
+ 1. **Sign up**: Visit [shopsavvy.com/data](https://shopsavvy.com/data)
53
+ 2. **Choose plan**: Select based on your usage needs
54
+ 3. **Get API key**: Copy from your dashboard
55
+ 4. **Test**: Run the 30-second example above
37
56
 
38
- ### Basic Usage
57
+ ### Environment Setup
39
58
 
40
59
  ```ruby
41
- require 'shopsavvy_data_api'
42
-
43
- # Initialize the client
44
- client = ShopsavvyDataApi.new(api_key: "ss_live_your_api_key_here")
60
+ # For production, use environment variables
61
+ ENV['SHOPSAVVY_API_KEY'] = 'ss_live_your_api_key_here'
45
62
 
46
- # Look up a product by barcode
47
- product = client.get_product_details("012345678901")
48
- puts product.data.name
49
-
50
- # Get current prices from all retailers
51
- offers = client.get_current_offers("012345678901")
52
- offers.data.each do |offer|
53
- puts "#{offer.retailer}: $#{offer.price}"
54
- end
55
-
56
- # Get price history
57
- history = client.get_price_history(
58
- "012345678901",
59
- "2024-01-01",
60
- "2024-01-31"
61
- )
63
+ # Initialize client
64
+ client = ShopsavvyDataApi.new(api_key: ENV['SHOPSAVVY_API_KEY'])
62
65
  ```
63
66
 
64
- ## šŸ“– API Reference
67
+ ## šŸ“– Complete API Reference
65
68
 
66
69
  ### Client Configuration
67
70
 
68
71
  ```ruby
69
- # Method 1: Simple initialization
72
+ # Basic configuration
73
+ client = ShopsavvyDataApi.new(
74
+ api_key: 'ss_live_your_api_key_here',
75
+ timeout: 30, # Request timeout in seconds
76
+ base_url: 'https://api.shopsavvy.com/v1' # Custom base URL
77
+ )
78
+
79
+ # Advanced configuration with retry logic
70
80
  client = ShopsavvyDataApi.new(
71
- api_key: "ss_live_your_api_key_here",
72
- timeout: 30, # optional
73
- base_url: "https://api.shopsavvy.com/v1" # optional
81
+ api_key: 'ss_live_your_api_key_here',
82
+ timeout: 60,
83
+ retry_attempts: 3,
84
+ retry_delay: 1.0,
85
+ user_agent: 'MyApp/1.0.0'
74
86
  )
75
87
 
76
- # Method 2: Using configuration object
88
+ # Configuration object approach
77
89
  config = ShopsavvyDataApi::Configuration.new(
78
- api_key: "ss_live_your_api_key_here",
79
- timeout: 60
90
+ api_key: 'ss_live_your_api_key_here',
91
+ timeout: 120,
92
+ debug: true # Enable debug logging
80
93
  )
81
94
  client = ShopsavvyDataApi.with_config(config)
82
-
83
- # Method 3: Direct client instantiation
84
- client = ShopsavvyDataApi::Client.new(api_key: "ss_live_your_api_key_here")
85
95
  ```
86
96
 
87
97
  ### Product Lookup
88
98
 
89
99
  #### Single Product
90
100
  ```ruby
91
- # Look up by barcode, ASIN, URL, or model number
101
+ # Look up by barcode, ASIN, URL, model number, or ShopSavvy ID
92
102
  product = client.get_product_details("012345678901")
93
103
  amazon_product = client.get_product_details("B08N5WRWNW")
94
104
  url_product = client.get_product_details("https://www.amazon.com/dp/B08N5WRWNW")
105
+ model_product = client.get_product_details("MQ023LL/A") # iPhone model number
95
106
 
96
107
  puts "Product: #{product.data.name}"
97
108
  puts "Brand: #{product.data.brand}"
98
109
  puts "Category: #{product.data.category}"
110
+ puts "ASIN: #{product.data.asin}" if product.data.asin
111
+ puts "Model: #{product.data.model}" if product.data.model
112
+ puts "Description: #{product.data.description}" if product.data.description
99
113
  ```
100
114
 
101
- #### Multiple Products
115
+ #### Bulk Product Lookup
102
116
  ```ruby
103
- products = client.get_product_details_batch([
104
- "012345678901",
105
- "B08N5WRWNW",
106
- "https://www.bestbuy.com/site/product/123456"
107
- ])
117
+ # Process up to 100 products at once (Pro plan)
118
+ identifiers = [
119
+ "012345678901", "B08N5WRWNW", "045496590048",
120
+ "https://www.bestbuy.com/site/product/123456",
121
+ "MQ023LL/A", "SM-S911U" # iPhone and Samsung model numbers
122
+ ]
123
+
124
+ products = client.get_product_details_batch(identifiers)
108
125
 
109
126
  products.data.each do |product|
110
- puts "#{product.name} by #{product.brand}"
127
+ puts "#{product.name} by #{product.brand} - #{product.category}"
128
+ puts " Identifiers: #{product.identifiers}" if product.identifiers
129
+ end
130
+
131
+ # Handle potential errors in batch processing
132
+ products.data.each_with_index do |product, index|
133
+ if product.nil?
134
+ puts "Failed to find product: #{identifiers[index]}"
135
+ else
136
+ puts "āœ“ Found: #{product.name}"
137
+ end
111
138
  end
112
139
  ```
113
140
 
114
- ### Current Pricing
141
+ ### Real-Time Pricing
115
142
 
116
- #### All Retailers
143
+ #### All Retailers Analysis
117
144
  ```ruby
118
145
  offers = client.get_current_offers("012345678901")
119
- puts "Found #{offers.data.length} offers"
146
+ puts "Found #{offers.data.length} offers across retailers"
120
147
 
121
- # Sort by price
148
+ # Advanced price analysis
122
149
  sorted_offers = offers.data.sort_by(&:price)
123
150
  cheapest = sorted_offers.first
124
- puts "Best price: #{cheapest.retailer} - $#{cheapest.price}"
151
+ most_expensive = sorted_offers.last
152
+
153
+ puts "šŸ’° Best price: #{cheapest.retailer} - $#{cheapest.price}"
154
+ puts "šŸ’ø Highest price: #{most_expensive.retailer} - $#{most_expensive.price}"
155
+ puts "šŸ“Š Average price: $#{offers.data.map(&:price).sum / offers.data.length}"
156
+ puts "šŸ’” Potential savings: $#{most_expensive.price - cheapest.price}"
157
+
158
+ # Filter by availability and condition
159
+ in_stock_offers = offers.data.select { |offer| offer.availability == 'in_stock' }
160
+ new_condition_offers = offers.data.select { |offer| offer.condition == 'new' }
161
+
162
+ puts "āœ… In-stock offers: #{in_stock_offers.length}"
163
+ puts "šŸ†• New condition: #{new_condition_offers.length}"
125
164
  ```
126
165
 
127
- #### Specific Retailer
166
+ #### Retailer-Specific Queries
128
167
  ```ruby
168
+ # Major retailers
129
169
  amazon_offers = client.get_current_offers("012345678901", retailer: "amazon")
170
+ walmart_offers = client.get_current_offers("012345678901", retailer: "walmart")
130
171
  target_offers = client.get_current_offers("012345678901", retailer: "target")
172
+ bestbuy_offers = client.get_current_offers("012345678901", retailer: "bestbuy")
173
+
174
+ # Compare specific retailers
175
+ retailers = %w[amazon walmart target bestbuy]
176
+ retailer_prices = {}
177
+
178
+ retailers.each do |retailer|
179
+ offers = client.get_current_offers("012345678901", retailer: retailer)
180
+ if offers.data.any?
181
+ best_offer = offers.data.min_by(&:price)
182
+ retailer_prices[retailer] = best_offer.price
183
+ end
184
+ end
185
+
186
+ puts "Retailer price comparison:"
187
+ retailer_prices.sort_by { |_, price| price }.each do |retailer, price|
188
+ puts " #{retailer.capitalize}: $#{price}"
189
+ end
131
190
  ```
132
191
 
133
- #### Multiple Products
192
+ #### Bulk Price Monitoring
134
193
  ```ruby
135
- batch_offers = client.get_current_offers_batch([
136
- "012345678901",
137
- "B08N5WRWNW"
138
- ])
194
+ # Monitor multiple products simultaneously
195
+ product_list = [
196
+ "012345678901", "B08N5WRWNW", "045496590048",
197
+ "B07XJ8C8F5", "B09G9FPHY6"
198
+ ]
199
+
200
+ batch_offers = client.get_current_offers_batch(product_list)
139
201
 
140
202
  batch_offers.data.each do |identifier, offers|
141
- puts "#{identifier}: #{offers.length} offers"
203
+ next if offers.empty?
204
+
205
+ best_offer = offers.min_by(&:price)
206
+ puts "#{identifier}:"
207
+ puts " Best price: #{best_offer.retailer} - $#{best_offer.price}"
208
+ puts " Total offers: #{offers.length}"
209
+ puts " In stock: #{offers.count { |o| o.availability == 'in_stock' }}"
210
+ puts
142
211
  end
143
212
  ```
144
213
 
145
- ### Price History
214
+ ### Historical Price Analysis
146
215
 
216
+ #### Comprehensive Price Trends
147
217
  ```ruby
148
- # Get 30 days of price history
218
+ require 'date'
219
+
220
+ # Get 90 days of price history for detailed analysis
221
+ end_date = Date.today
222
+ start_date = end_date - 90
223
+
149
224
  history = client.get_price_history(
150
225
  "012345678901",
151
- "2024-01-01",
152
- "2024-01-31"
226
+ start_date.strftime("%Y-%m-%d"),
227
+ end_date.strftime("%Y-%m-%d")
153
228
  )
154
229
 
230
+ puts "šŸ“ˆ 90-Day Price Analysis"
231
+ puts "=" * 50
232
+
155
233
  history.data.each do |offer|
156
- puts "#{offer.retailer}:"
157
- puts " Current price: $#{offer.price}"
158
- puts " Historical data points: #{offer.price_history.length}"
159
- puts " Average price: $#{offer.average_price.round(2)}" if offer.average_price
160
- puts " Price range: $#{offer.min_price} - $#{offer.max_price}"
234
+ next if offer.price_history.empty?
235
+
236
+ prices = offer.price_history.map(&:price)
237
+ current_price = offer.price
238
+
239
+ # Statistical analysis
240
+ avg_price = prices.sum.to_f / prices.length
241
+ min_price = prices.min
242
+ max_price = prices.max
243
+
244
+ # Price trend calculation
245
+ recent_prices = prices.last(7) # Last week
246
+ older_prices = prices.first([prices.length - 7, 1].max)
247
+
248
+ trend = if recent_prices.any? && older_prices.any?
249
+ recent_avg = recent_prices.sum.to_f / recent_prices.length
250
+ older_avg = older_prices.sum.to_f / older_prices.length
251
+ change_pct = ((recent_avg - older_avg) / older_avg * 100).round(1)
252
+
253
+ if change_pct > 5
254
+ "šŸ“ˆ Rising (+#{change_pct}%)"
255
+ elsif change_pct < -5
256
+ "šŸ“‰ Falling (#{change_pct}%)"
257
+ else
258
+ "šŸ“Š Stable (#{change_pct}%)"
259
+ end
260
+ else
261
+ "šŸ“Š Insufficient data"
262
+ end
263
+
264
+ puts "šŸŖ #{offer.retailer.upcase}"
265
+ puts " Current: $#{current_price}"
266
+ puts " Average: $#{avg_price.round(2)}"
267
+ puts " Range: $#{min_price} - $#{max_price}"
268
+ puts " Savings opportunity: $#{(current_price - min_price).round(2)}"
269
+ puts " Trend: #{trend}"
270
+ puts " Data points: #{offer.price_history.length}"
271
+ puts
161
272
  end
273
+ ```
162
274
 
163
- # Get retailer-specific price history
164
- amazon_history = client.get_price_history(
165
- "012345678901",
166
- "2024-01-01",
167
- "2024-01-31",
168
- retailer: "amazon"
169
- )
275
+ #### Retailer-Specific Historical Analysis
276
+ ```ruby
277
+ # Compare price history across major retailers
278
+ retailers = %w[amazon walmart target bestbuy]
279
+ historical_comparison = {}
280
+
281
+ retailers.each do |retailer|
282
+ history = client.get_price_history(
283
+ "012345678901",
284
+ "2024-01-01",
285
+ "2024-12-31",
286
+ retailer: retailer
287
+ )
288
+
289
+ next if history.data.empty?
290
+
291
+ offer = history.data.first
292
+ if offer.price_history.any?
293
+ prices = offer.price_history.map(&:price)
294
+ historical_comparison[retailer] = {
295
+ current: offer.price,
296
+ average: prices.sum.to_f / prices.length,
297
+ lowest: prices.min,
298
+ highest: prices.max,
299
+ volatility: prices.max - prices.min
300
+ }
301
+ end
302
+ end
303
+
304
+ puts "Retailer Historical Comparison:"
305
+ historical_comparison.each do |retailer, data|
306
+ puts "#{retailer.capitalize}:"
307
+ puts " Current: $#{data[:current]}"
308
+ puts " Average: $#{data[:average].round(2)}"
309
+ puts " Best ever: $#{data[:lowest]}"
310
+ puts " Worst: $#{data[:highest]}"
311
+ puts " Volatility: $#{data[:volatility].round(2)}"
312
+ puts
313
+ end
170
314
  ```
171
315
 
172
316
  ### Product Monitoring
@@ -316,248 +460,477 @@ response_hash = product.to_h
316
460
  puts response_hash[:data][:name]
317
461
  ```
318
462
 
319
- ## šŸ’” Examples
463
+ ## šŸš€ Production Deployment
464
+
465
+ ### Ruby on Rails Integration
320
466
 
321
- ### Price Comparison Tool
322
467
  ```ruby
323
- def compare_prices(client, identifier)
324
- offers = client.get_current_offers(identifier)
325
-
326
- if offers.data.empty?
327
- puts "No offers found"
328
- return
468
+ # Gemfile
469
+ gem 'shopsavvy-sdk'
470
+ gem 'sidekiq' # For background jobs
471
+
472
+ # config/application.rb
473
+ config.shopsavvy_api_key = Rails.application.credentials.shopsavvy_api_key
474
+
475
+ # app/services/price_tracking_service.rb
476
+ class PriceTrackingService
477
+ def initialize
478
+ @client = ShopsavvyDataApi.new(
479
+ api_key: Rails.application.config.shopsavvy_api_key,
480
+ timeout: 60
481
+ )
329
482
  end
483
+
484
+ def track_product(product_id, target_price)
485
+ # Schedule monitoring
486
+ @client.schedule_product_monitoring(product_id, 'daily')
487
+
488
+ # Create local tracking record
489
+ PriceAlert.create!(
490
+ product_identifier: product_id,
491
+ target_price: target_price,
492
+ status: 'active'
493
+ )
494
+ end
495
+
496
+ def check_price_alerts
497
+ PriceAlert.active.find_each do |alert|
498
+ CheckPriceAlertJob.perform_later(alert.id)
499
+ end
500
+ end
501
+ end
502
+
503
+ # app/jobs/check_price_alert_job.rb
504
+ class CheckPriceAlertJob < ApplicationJob
505
+ queue_as :default
330
506
 
331
- sorted_offers = offers.data.sort_by(&:price)
332
- cheapest = sorted_offers.first
333
- most_expensive = sorted_offers.last
334
-
335
- puts "šŸ† Best price: #{cheapest.retailer} - $#{cheapest.price}"
336
- puts "šŸ’ø Highest price: #{most_expensive.retailer} - $#{most_expensive.price}"
337
- puts "šŸ’° Potential savings: $#{most_expensive.price - cheapest.price}"
338
-
339
- {
340
- best_offer: cheapest,
341
- worst_offer: most_expensive,
342
- savings: most_expensive.price - cheapest.price
343
- }
507
+ def perform(alert_id)
508
+ alert = PriceAlert.find(alert_id)
509
+ client = ShopsavvyDataApi.new(api_key: Rails.application.config.shopsavvy_api_key)
510
+
511
+ offers = client.get_current_offers(alert.product_identifier)
512
+ best_offer = offers.data.min_by(&:price)
513
+
514
+ if best_offer && best_offer.price <= alert.target_price
515
+ # Send notification
516
+ PriceAlertMailer.target_reached(alert, best_offer).deliver_now
517
+ alert.update!(status: 'triggered', triggered_at: Time.current)
518
+ end
519
+ rescue ShopsavvyDataApi::Error => e
520
+ Rails.logger.error "ShopSavvy API error: #{e.message}"
521
+ # Optionally retry or alert administrators
522
+ end
344
523
  end
524
+ ```
345
525
 
346
- # Usage
347
- client = ShopsavvyDataApi.new(api_key: "your_api_key")
348
- comparison = compare_prices(client, "012345678901")
526
+ ### Sinatra Microservice
527
+
528
+ ```ruby
529
+ # app.rb
530
+ require 'sinatra'
531
+ require 'json'
532
+ require 'shopsavvy_data_api'
533
+
534
+ class PriceAPI < Sinatra::Base
535
+ configure do
536
+ set :shopsavvy_client, ShopsavvyDataApi.new(
537
+ api_key: ENV['SHOPSAVVY_API_KEY'],
538
+ timeout: 30
539
+ )
540
+ end
541
+
542
+ before do
543
+ content_type :json
544
+ end
545
+
546
+ get '/api/product/:identifier/price' do
547
+ identifier = params[:identifier]
548
+
549
+ begin
550
+ offers = settings.shopsavvy_client.get_current_offers(identifier)
551
+
552
+ {
553
+ success: true,
554
+ product_id: identifier,
555
+ offers: offers.data.map do |offer|
556
+ {
557
+ retailer: offer.retailer,
558
+ price: offer.price,
559
+ availability: offer.availability,
560
+ condition: offer.condition,
561
+ url: offer.url
562
+ }
563
+ end,
564
+ best_price: offers.data.min_by(&:price)&.price,
565
+ credits_remaining: offers.credits_remaining
566
+ }.to_json
567
+ rescue ShopsavvyDataApi::Error => e
568
+ status 400
569
+ { success: false, error: e.message }.to_json
570
+ end
571
+ end
572
+
573
+ get '/api/product/:identifier/history' do
574
+ identifier = params[:identifier]
575
+ days = (params[:days] || 30).to_i
576
+
577
+ end_date = Date.today
578
+ start_date = end_date - days
579
+
580
+ begin
581
+ history = settings.shopsavvy_client.get_price_history(
582
+ identifier,
583
+ start_date.strftime('%Y-%m-%d'),
584
+ end_date.strftime('%Y-%m-%d')
585
+ )
586
+
587
+ {
588
+ success: true,
589
+ product_id: identifier,
590
+ period: "#{days} days",
591
+ data: history.data
592
+ }.to_json
593
+ rescue ShopsavvyDataApi::Error => e
594
+ status 400
595
+ { success: false, error: e.message }.to_json
596
+ end
597
+ end
598
+ end
349
599
  ```
350
600
 
351
- ### Price Alert System
601
+ ### Background Processing with Sidekiq
602
+
352
603
  ```ruby
353
- def setup_price_alert(client, identifier, target_price)
354
- # Schedule daily monitoring
355
- client.schedule_product_monitoring(identifier, "daily")
356
-
357
- # Check current prices
358
- offers = client.get_current_offers(identifier)
359
- best_offer = offers.data.min_by(&:price)
360
-
361
- if best_offer.price <= target_price
362
- puts "šŸŽ‰ Target price reached!"
363
- puts "šŸ’ø #{best_offer.retailer}: $#{best_offer.price}"
364
- puts "šŸ”— Buy now: #{best_offer.url}"
365
- true
366
- else
367
- puts "ā° Monitoring #{identifier}"
368
- puts "šŸ’° Current best: $#{best_offer.price} (target: $#{target_price})"
369
- puts "šŸ“ˆ Need $#{(best_offer.price - target_price).round(2)} price drop"
370
- false
604
+ # lib/price_monitor.rb
605
+ class PriceMonitor
606
+ include Sidekiq::Worker
607
+ sidekiq_options retry: 3, backtrace: true
608
+
609
+ def perform(product_ids)
610
+ client = ShopsavvyDataApi.new(api_key: ENV['SHOPSAVVY_API_KEY'])
611
+
612
+ product_ids.each do |product_id|
613
+ begin
614
+ # Get current prices
615
+ offers = client.get_current_offers(product_id)
616
+ next if offers.data.empty?
617
+
618
+ # Store in database or cache
619
+ best_price = offers.data.min_by(&:price).price
620
+ Redis.current.setex("price:#{product_id}", 3600, best_price)
621
+
622
+ # Check for alerts
623
+ check_price_alerts(product_id, best_price)
624
+
625
+ rescue ShopsavvyDataApi::RateLimitError => e
626
+ # Exponential backoff
627
+ self.class.perform_in(2 ** sidekiq_options['retry_count'], [product_id])
628
+ raise e
629
+ rescue ShopsavvyDataApi::Error => e
630
+ logger.error "API error for #{product_id}: #{e.message}"
631
+ end
632
+ end
633
+ end
634
+
635
+ private
636
+
637
+ def check_price_alerts(product_id, current_price)
638
+ # Implementation for checking and triggering alerts
371
639
  end
372
640
  end
373
641
 
374
- # Usage
375
- client = ShopsavvyDataApi.new(api_key: "your_api_key")
376
- setup_price_alert(client, "012345678901", 299.99)
642
+ # Schedule regular monitoring
643
+ PriceMonitor.perform_async(['012345678901', 'B08N5WRWNW'])
377
644
  ```
378
645
 
379
- ### Historical Price Analysis
646
+ ## šŸ’” Real-World Use Cases
647
+
648
+ ### E-commerce Price Intelligence
380
649
  ```ruby
381
- require 'date'
650
+ # Comprehensive competitive analysis tool
651
+ class CompetitiveAnalyzer
652
+ def initialize(api_key)
653
+ @client = ShopsavvyDataApi.new(api_key: api_key)
654
+ end
382
655
 
383
- def analyze_price_trends(client, identifier, days = 30)
384
- end_date = Date.today
385
- start_date = end_date - days
386
-
387
- history = client.get_price_history(
388
- identifier,
389
- start_date.strftime("%Y-%m-%d"),
390
- end_date.strftime("%Y-%m-%d")
391
- )
392
-
393
- analysis = {}
394
-
395
- history.data.each do |offer|
396
- next if offer.price_history.empty?
656
+ def analyze_market(product_ids, competitors = %w[amazon walmart target bestbuy])
657
+ analysis = {}
397
658
 
398
- prices = offer.price_history.map(&:price)
659
+ product_ids.each do |product_id|
660
+ product_analysis = analyze_product_competition(product_id, competitors)
661
+ analysis[product_id] = product_analysis
662
+ end
399
663
 
400
- # Calculate trend
401
- recent_prices = prices.last(7) # Last week
402
- older_prices = prices.first(prices.length - 7) # Everything else
664
+ generate_competitive_report(analysis)
665
+ end
666
+
667
+ private
668
+
669
+ def analyze_product_competition(product_id, competitors)
670
+ # Get product details
671
+ product = @client.get_product_details(product_id)
403
672
 
404
- trend = if recent_prices.length > 0 && older_prices.length > 0
405
- recent_avg = recent_prices.sum.to_f / recent_prices.length
406
- older_avg = older_prices.sum.to_f / older_prices.length
407
- recent_avg > older_avg ? "šŸ“ˆ Rising" : "šŸ“‰ Falling"
408
- else
409
- "šŸ“Š Insufficient data"
410
- end
673
+ # Get current offers from all retailers
674
+ all_offers = @client.get_current_offers(product_id)
675
+
676
+ # Filter by target competitors
677
+ competitor_offers = all_offers.data.select do |offer|
678
+ competitors.include?(offer.retailer.downcase)
679
+ end
680
+
681
+ # Price analysis
682
+ prices = competitor_offers.map(&:price)
411
683
 
412
- analysis[offer.retailer] = {
413
- current_price: offer.price,
414
- average_price: offer.average_price,
415
- min_price: offer.min_price,
416
- max_price: offer.max_price,
417
- data_points: offer.price_history.length,
418
- trend: trend
684
+ {
685
+ product_name: product.data.name,
686
+ brand: product.data.brand,
687
+ total_offers: all_offers.data.length,
688
+ competitor_offers: competitor_offers.length,
689
+ price_range: {
690
+ min: prices.min,
691
+ max: prices.max,
692
+ average: prices.sum.to_f / prices.length
693
+ },
694
+ market_position: calculate_market_position(competitor_offers),
695
+ availability_score: calculate_availability_score(competitor_offers)
419
696
  }
420
697
  end
421
-
422
- analysis
698
+
699
+ def calculate_market_position(offers)
700
+ return 'No data' if offers.empty?
701
+
702
+ prices = offers.map(&:price).sort
703
+ median_price = prices[prices.length / 2]
704
+
705
+ case median_price
706
+ when 0..50 then 'Budget'
707
+ when 50..200 then 'Mid-range'
708
+ when 200..500 then 'Premium'
709
+ else 'Luxury'
710
+ end
711
+ end
712
+
713
+ def calculate_availability_score(offers)
714
+ return 0 if offers.empty?
715
+
716
+ in_stock_count = offers.count { |offer| offer.availability == 'in_stock' }
717
+ (in_stock_count.to_f / offers.length * 100).round(1)
718
+ end
423
719
  end
424
720
 
425
721
  # Usage
426
- client = ShopsavvyDataApi.new(api_key: "your_api_key")
427
- trends = analyze_price_trends(client, "012345678901", 60)
428
-
429
- trends.each do |retailer, data|
430
- puts "#{retailer}:"
431
- puts " Current: $#{data[:current_price]}"
432
- puts " Average: $#{data[:average_price].round(2)}"
433
- puts " Range: $#{data[:min_price]} - $#{data[:max_price]}"
434
- puts " Trend: #{data[:trend]}"
435
- puts
436
- end
722
+ analyzer = CompetitiveAnalyzer.new(ENV['SHOPSAVVY_API_KEY'])
723
+ report = analyzer.analyze_market([
724
+ '012345678901', 'B08N5WRWNW', '045496590048'
725
+ ])
437
726
  ```
438
727
 
439
- ### Bulk Product Monitoring
728
+ ### Inventory Management Integration
440
729
  ```ruby
441
- def setup_bulk_monitoring(client, identifiers, frequency = "daily")
442
- # Schedule all products
443
- result = client.schedule_product_monitoring_batch(identifiers, frequency)
444
-
445
- successful = []
446
- failed = []
447
-
448
- result.data.each do |item|
449
- if item["scheduled"]
450
- successful << item["identifier"]
451
- else
452
- failed << item["identifier"]
730
+ # Integration with inventory management system
731
+ class InventoryPriceManager
732
+ def initialize(api_key)
733
+ @client = ShopsavvyDataApi.new(api_key: api_key)
734
+ end
735
+
736
+ def update_competitive_pricing(inventory_items)
737
+ pricing_updates = []
738
+
739
+ inventory_items.each do |item|
740
+ next unless item.competitor_tracking_enabled?
741
+
742
+ begin
743
+ # Get current market prices
744
+ offers = @client.get_current_offers(item.barcode)
745
+ next if offers.data.empty?
746
+
747
+ # Calculate competitive price point
748
+ competitor_prices = offers.data.map(&:price)
749
+ market_analysis = analyze_market_prices(competitor_prices)
750
+
751
+ suggested_price = calculate_competitive_price(
752
+ item.cost_price,
753
+ market_analysis,
754
+ item.target_margin
755
+ )
756
+
757
+ pricing_updates << {
758
+ item_id: item.id,
759
+ current_price: item.selling_price,
760
+ suggested_price: suggested_price,
761
+ market_analysis: market_analysis,
762
+ reasoning: generate_pricing_reasoning(item, market_analysis, suggested_price)
763
+ }
764
+
765
+ rescue ShopsavvyDataApi::Error => e
766
+ Rails.logger.error "Pricing update failed for #{item.id}: #{e.message}"
767
+ next
768
+ end
453
769
  end
770
+
771
+ pricing_updates
454
772
  end
455
-
456
- puts "āœ… Successfully scheduled: #{successful.length} products"
457
- puts "āŒ Failed to schedule: #{failed.length} products"
458
-
459
- if failed.any?
460
- puts "Failed products:"
461
- failed.each { |identifier| puts " - #{identifier}" }
773
+
774
+ private
775
+
776
+ def analyze_market_prices(prices)
777
+ sorted_prices = prices.sort
778
+ {
779
+ min: sorted_prices.first,
780
+ max: sorted_prices.last,
781
+ median: sorted_prices[sorted_prices.length / 2],
782
+ average: prices.sum.to_f / prices.length,
783
+ percentile_25: sorted_prices[(sorted_prices.length * 0.25).round],
784
+ percentile_75: sorted_prices[(sorted_prices.length * 0.75).round]
785
+ }
462
786
  end
463
-
464
- { successful: successful, failed: failed }
465
- end
466
787
 
467
- # Usage
468
- client = ShopsavvyDataApi.new(api_key: "your_api_key")
469
- products_to_monitor = [
470
- "012345678901",
471
- "B08N5WRWNW",
472
- "045496596439"
473
- ]
474
- setup_bulk_monitoring(client, products_to_monitor, "daily")
788
+ def calculate_competitive_price(cost_price, market_analysis, target_margin)
789
+ min_price = cost_price * (1 + target_margin)
790
+ competitive_price = market_analysis[:percentile_25] * 0.95 # 5% under 25th percentile
791
+
792
+ [min_price, competitive_price].max.round(2)
793
+ end
794
+ end
475
795
  ```
476
796
 
477
- ### Rails Integration Example
797
+ ### Market Research Analytics
478
798
  ```ruby
479
- # app/models/price_tracker.rb
480
- class PriceTracker < ApplicationRecord
481
- validates :product_identifier, presence: true
482
- validates :target_price, presence: true, numericality: { greater_than: 0 }
483
-
484
- def self.check_all_alerts
485
- client = ShopsavvyDataApi.new(api_key: Rails.application.credentials.shopsavvy_api_key)
799
+ # Advanced market research and trend analysis
800
+ class MarketResearcher
801
+ def initialize(api_key)
802
+ @client = ShopsavvyDataApi.new(api_key: api_key)
803
+ end
804
+
805
+ def research_category_trends(product_categories, time_periods)
806
+ research_report = {}
486
807
 
487
- PriceTracker.active.find_each do |tracker|
488
- tracker.check_price_alert(client)
808
+ product_categories.each do |category, product_list|
809
+ category_data = analyze_category_trends(product_list, time_periods)
810
+ research_report[category] = category_data
489
811
  end
812
+
813
+ generate_market_intelligence_report(research_report)
490
814
  end
491
-
492
- def check_price_alert(client)
493
- offers = client.get_current_offers(product_identifier)
494
- best_offer = offers.data.min_by(&:price)
815
+
816
+ def track_seasonal_patterns(product_id, months = 12)
817
+ patterns = {}
495
818
 
496
- if best_offer && best_offer.price <= target_price
497
- # Send notification
498
- PriceAlertMailer.target_price_reached(self, best_offer).deliver_later
499
- update!(alert_triggered: true, triggered_at: Time.current)
819
+ (0...months).each do |month_offset|
820
+ end_date = Date.today - (month_offset * 30)
821
+ start_date = end_date - 30
822
+
823
+ history = @client.get_price_history(
824
+ product_id,
825
+ start_date.strftime('%Y-%m-%d'),
826
+ end_date.strftime('%Y-%m-%d')
827
+ )
828
+
829
+ month_name = end_date.strftime('%B %Y')
830
+ patterns[month_name] = analyze_monthly_patterns(history.data)
500
831
  end
501
- rescue ShopsavvyDataApi::Error => e
502
- Rails.logger.error "ShopSavvy API error for tracker #{id}: #{e.message}"
832
+
833
+ identify_seasonal_trends(patterns)
503
834
  end
504
- end
505
835
 
506
- # Usage in rake task or background job
507
- # bin/rails runner "PriceTracker.check_all_alerts"
836
+ private
837
+
838
+ def analyze_monthly_patterns(history_data)
839
+ return { average_price: 0, volatility: 0 } if history_data.empty?
840
+
841
+ all_prices = []
842
+
843
+ history_data.each do |offer|
844
+ next if offer.price_history.empty?
845
+ all_prices.concat(offer.price_history.map(&:price))
846
+ end
847
+
848
+ return { average_price: 0, volatility: 0 } if all_prices.empty?
849
+
850
+ average = all_prices.sum.to_f / all_prices.length
851
+ variance = all_prices.map { |price| (price - average) ** 2 }.sum / all_prices.length
852
+
853
+ {
854
+ average_price: average.round(2),
855
+ volatility: Math.sqrt(variance).round(2),
856
+ price_points: all_prices.length
857
+ }
858
+ end
859
+ end
508
860
  ```
509
861
 
510
- ## šŸ› ļø Development
862
+ ## šŸ› ļø Development & Testing
511
863
 
512
- ### Installing for Development
864
+ ### Local Development Setup
513
865
 
514
- ```bash
515
- git clone https://github.com/shopsavvy/ruby-sdk
516
- cd ruby-sdk
866
+ ```ruby
867
+ # Clone the repository
868
+ git clone https://github.com/shopsavvy/sdk-ruby.git
869
+ cd sdk-ruby
870
+
871
+ # Install dependencies
517
872
  bundle install
518
- ```
519
873
 
520
- ### Running Tests
874
+ # Set up environment variables
875
+ echo 'SHOPSAVVY_API_KEY=ss_test_your_test_key_here' > .env
521
876
 
522
- ```bash
523
- # Run all tests
877
+ # Run tests
524
878
  bundle exec rspec
525
879
 
526
- # Run with coverage
527
- bundle exec rspec --format documentation
528
-
529
- # Run specific test file
530
- bundle exec rspec spec/client_spec.rb
880
+ # Run linting
881
+ bundle exec rubocop
531
882
 
532
- # Run with verbose output
533
- bundle exec rspec --format documentation --color
883
+ # Generate documentation
884
+ bundle exec yard doc
534
885
  ```
535
886
 
536
- ### Code Quality
537
-
538
- ```bash
539
- # Linting
540
- bundle exec rubocop
887
+ ### Testing Your Integration
541
888
 
542
- # Auto-fix issues
543
- bundle exec rubocop -A
889
+ ```ruby
890
+ # Create a test script
891
+ require 'shopsavvy_data_api'
544
892
 
545
- # Type checking (if using Sorbet)
546
- bundle exec srb tc
893
+ # Use test API key (starts with ss_test_)
894
+ client = ShopsavvyDataApi.new(api_key: 'ss_test_your_test_key_here')
547
895
 
548
- # Generate documentation
549
- bundle exec yard doc
896
+ # Test basic functionality
897
+ begin
898
+ # Test product lookup
899
+ product = client.get_product_details('012345678901')
900
+ puts "āœ… Product lookup: #{product.data.name}"
901
+
902
+ # Test current offers
903
+ offers = client.get_current_offers('012345678901')
904
+ puts "āœ… Current offers: #{offers.data.length} found"
905
+
906
+ # Test usage info
907
+ usage = client.get_usage
908
+ puts "āœ… API usage: #{usage.data.credits_remaining} credits remaining"
909
+
910
+ puts "\nšŸŽ‰ All tests passed! SDK is working correctly."
911
+
912
+ rescue ShopsavvyDataApi::Error => e
913
+ puts "āŒ Test failed: #{e.message}"
914
+ end
550
915
  ```
551
916
 
552
- ### Contributing
917
+ ## šŸ“š Additional Resources
553
918
 
554
- See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on contributing to this project.
919
+ - **[ShopSavvy Data API Documentation](https://shopsavvy.com/data/documentation)** - Complete API reference
920
+ - **[API Dashboard](https://shopsavvy.com/data/dashboard)** - Manage your API keys and usage
921
+ - **[GitHub Repository](https://github.com/shopsavvy/sdk-ruby)** - Source code and issues
922
+ - **[RubyGems Page](https://rubygems.org/gems/shopsavvy-sdk)** - Gem releases and stats
923
+ - **[Support](mailto:business@shopsavvy.com)** - Get help from our team
555
924
 
556
- ## šŸ“š Additional Resources
925
+ ## šŸ¤ Contributing
926
+
927
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details on:
557
928
 
558
- - [ShopSavvy Data API Documentation](https://shopsavvy.com/data/documentation)
559
- - [API Dashboard](https://shopsavvy.com/data/dashboard)
560
- - [Support](mailto:business@shopsavvy.com)
929
+ - Reporting bugs
930
+ - Suggesting enhancements
931
+ - Submitting pull requests
932
+ - Development workflow
933
+ - Code standards
561
934
 
562
935
  ## šŸ“„ License
563
936
 
@@ -565,10 +938,23 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
565
938
 
566
939
  ## šŸ¢ About ShopSavvy
567
940
 
568
- ShopSavvy is a price comparison and shopping app that helps users find the best deals on products across various retailers. Since 2008, ShopSavvy has been downloaded over 40 million times and helps millions of users save money every day.
941
+ **ShopSavvy** is the world's first mobile shopping app, helping consumers find the best deals since 2008. With over **40 million downloads** and millions of active users, ShopSavvy has saved consumers billions of dollars.
942
+
943
+ ### Our Data API Powers:
944
+ - šŸ›’ **E-commerce platforms** with competitive intelligence
945
+ - šŸ“Š **Market research** with real-time pricing data
946
+ - šŸŖ **Retailers** with inventory and pricing optimization
947
+ - šŸ“± **Mobile apps** with product lookup and price comparison
948
+ - šŸ¤– **Business intelligence** with automated price monitoring
569
949
 
570
- Our Data API provides the same powerful product data and pricing intelligence that powers our consumer app, available to developers and businesses worldwide.
950
+ ### Why Choose ShopSavvy Data API?
951
+ - āœ… **Trusted by millions** - Proven at scale since 2008
952
+ - āœ… **Comprehensive coverage** - 1000+ retailers, millions of products
953
+ - āœ… **Real-time accuracy** - Fresh data updated continuously
954
+ - āœ… **Developer-friendly** - Easy integration, great documentation
955
+ - āœ… **Reliable infrastructure** - 99.9% uptime, enterprise-grade
956
+ - āœ… **Flexible pricing** - Plans for every use case and budget
571
957
 
572
958
  ---
573
959
 
574
- **Need help?** Contact us at [business@shopsavvy.com](mailto:business@shopsavvy.com) or visit [shopsavvy.com/data](https://shopsavvy.com/data) for more information.
960
+ **Ready to get started?** [Sign up for your API key](https://shopsavvy.com/data) • **Need help?** [Contact us](mailto:business@shopsavvy.com)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ShopsavvyDataApi
4
- VERSION = "1.0.0"
4
+ VERSION = "1.0.1"
5
5
  end
metadata CHANGED
@@ -1,11 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shopsavvy-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - ShopSavvy by Monolith Technologies, Inc.
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
10
  date: 2025-07-30 00:00:00.000000000 Z
@@ -2927,7 +2926,6 @@ metadata:
2927
2926
  changelog_uri: https://github.com/shopsavvy/sdk-ruby/blob/main/CHANGELOG.md
2928
2927
  documentation_uri: https://shopsavvy.com/data/documentation
2929
2928
  bug_tracker_uri: https://github.com/shopsavvy/sdk-ruby/issues
2930
- post_install_message:
2931
2929
  rdoc_options: []
2932
2930
  require_paths:
2933
2931
  - lib
@@ -2942,8 +2940,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
2942
2940
  - !ruby/object:Gem::Version
2943
2941
  version: '0'
2944
2942
  requirements: []
2945
- rubygems_version: 3.0.3.1
2946
- signing_key:
2943
+ rubygems_version: 3.6.2
2947
2944
  specification_version: 4
2948
2945
  summary: Official Ruby SDK for ShopSavvy Data API
2949
2946
  test_files: []