resilientlink 1.0.0

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.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +83 -0
  3. data/lib/resilientlink.rb +117 -0
  4. metadata +46 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: dba098b1c03af355a388cf7f641024563505947a79641f22bc3e18a765dd1395
4
+ data.tar.gz: bff0570aea14931fa8b7380901b1862523352cfb9ec8cb592ac3f6acada8dee9
5
+ SHA512:
6
+ metadata.gz: 99289b1ea9a89b2329e20aba7b0fd9eb3db042481ba8718a6cc4d1ecd46985222fe90a93fa68a132918ce111fdcdfc11323d00d8c6dad119b0346301d5562f36
7
+ data.tar.gz: 5a1ab1c3ae48d811101a733bb93f102a9075ee6bc795460e15305f1827d906c649b20608ba7cadc76e11de9d5f0cd600678963d4f4ddb34b1b0cf471d511340c
data/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # ResilientLink Ruby SDK
2
+
3
+ Official Ruby gem for the [ResilientLink](https://resilientlink.silentgode.com) Web Scraping API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ gem install resilientlink
9
+ ```
10
+
11
+ Or add to your `Gemfile`:
12
+
13
+ ```ruby
14
+ gem 'resilientlink'
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```ruby
20
+ require 'resilientlink'
21
+
22
+ client = Resilientlink::Client.new(api_key: 'YOUR_API_KEY')
23
+
24
+ result = client.scrape('https://example.com')
25
+
26
+ puts result['data']['title'] # "Example Domain"
27
+ puts result['data']['description'] # meta description
28
+ puts result['data']['image'] # OG image URL
29
+ ```
30
+
31
+ ## Options
32
+
33
+ ```ruby
34
+ result = client.scrape('https://example.com',
35
+ return_html: true, # include raw HTML
36
+ screenshot: true, # base64 PNG (Pro/Enterprise)
37
+ pdf: true, # base64 PDF (Pro/Enterprise)
38
+ pdf_format: 'A4',
39
+ bypass_cache: true, # force fresh scrape
40
+ js_render: true, # JS rendering (Pro/Enterprise)
41
+ wait_for_selector: '#app', # wait for CSS selector
42
+ wait_ms: 2000, # wait 2s before scraping
43
+ custom_headers: { 'Accept-Language' => 'en-US' },
44
+ timeout: 30_000 # ms (max 60000)
45
+ )
46
+ ```
47
+
48
+ ## Response
49
+
50
+ ```ruby
51
+ {
52
+ 'success' => true,
53
+ 'cached' => false,
54
+ 'tier' => '...',
55
+ 'responseTime' => 412,
56
+ 'data' => {
57
+ 'url' => 'https://example.com',
58
+ 'title' => 'Example Domain',
59
+ 'description' => '...',
60
+ 'image' => '...',
61
+ 'domain' => 'example.com',
62
+ 'og' => { 'title' => '...', 'description' => '...' },
63
+ 'content' => { 'wordCount' => 423, 'readTimeMinutes' => 2 },
64
+ 'scrapedAt' => '2026-...'
65
+ }
66
+ }
67
+ ```
68
+
69
+ ## Error Handling
70
+
71
+ ```ruby
72
+ begin
73
+ result = client.scrape('https://example.com')
74
+ rescue Resilientlink::Error => e
75
+ puts e.message # human-readable error
76
+ puts e.status_code # 429 = rate limit, 401 = bad key, 451 = blocked
77
+ puts e.body # full response hash
78
+ end
79
+ ```
80
+
81
+ ## Get Your API Key
82
+
83
+ Sign up at [resilientlink](https://resilientlink.silentgode.com) → Dashboard → API Key.
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'json'
6
+
7
+ module Resilientlink
8
+ VERSION = '1.0.0'
9
+ DEFAULT_URL = 'https://api.resilientlink.silentgode.com'
10
+ DEFAULT_TIMEOUT = 60
11
+
12
+ # Raised when the ResilientLink API returns an error.
13
+ class Error < StandardError
14
+ attr_reader :status_code, :body
15
+
16
+ def initialize(message, status_code: nil, body: nil)
17
+ super(message)
18
+ @status_code = status_code
19
+ @body = body
20
+ end
21
+ end
22
+
23
+ class Client
24
+ # @param api_key [String] Your ResilientLink API key (required)
25
+ # @param base_url [String] Override API base URL
26
+ # @param timeout [Integer] Request timeout in seconds (default: 60)
27
+ def initialize(api_key:, base_url: DEFAULT_URL, timeout: DEFAULT_TIMEOUT)
28
+ raise ArgumentError, 'api_key is required.' if api_key.nil? || api_key.empty?
29
+
30
+ @api_key = api_key
31
+ @base_url = base_url.chomp('/')
32
+ @timeout = timeout
33
+ end
34
+
35
+ # Scrape a URL and return structured metadata.
36
+ #
37
+ # @param url [String] The URL to scrape (required)
38
+ # @param opts [Hash] Scrape options:
39
+ # :return_html [Boolean] Include raw HTML in response
40
+ # :screenshot [Boolean] Return base64 PNG screenshot (Pro/Enterprise)
41
+ # :full_page [Boolean] Full-page screenshot (default: true)
42
+ # :pdf [Boolean] Return base64 PDF (Pro/Enterprise)
43
+ # :pdf_format [String] 'A4', 'Letter', etc. (default: 'A4')
44
+ # :pdf_background [Boolean] Include background (default: true)
45
+ # :pdf_landscape [Boolean] Landscape PDF
46
+ # :bypass_cache [Boolean] Skip cache, force fresh scrape
47
+ # :js_render [Boolean] Enable JS rendering (Pro/Enterprise)
48
+ # :wait_for_selector [String] CSS selector to wait for
49
+ # :wait_until [String] 'networkidle0', 'load', 'domcontentloaded'
50
+ # :wait_ms [Integer] Extra ms to wait before scraping
51
+ # :custom_headers [Hash] HTTP headers to forward
52
+ # :custom_js [String] JavaScript to execute (Enterprise)
53
+ # :return_cookies [Boolean] Return cookies from the page
54
+ # :block_resources [Array] Resource types to block: ['media','font']
55
+ # :timeout [Integer] Per-request timeout in ms (max 60000)
56
+ #
57
+ # @return [Hash] Scrape result
58
+ # @raise [Resilientlink::Error]
59
+ def scrape(url, **opts)
60
+ raise ArgumentError, 'url is required.' if url.nil? || url.empty?
61
+
62
+ body = { url: url }
63
+ body[:return_html] = opts[:return_html] unless opts[:return_html].nil?
64
+ body[:screenshot] = opts[:screenshot] unless opts[:screenshot].nil?
65
+ body[:full_page] = opts[:full_page] unless opts[:full_page].nil?
66
+ body[:pdf] = opts[:pdf] unless opts[:pdf].nil?
67
+ body[:pdf_format] = opts[:pdf_format] unless opts[:pdf_format].nil?
68
+ body[:pdf_background] = opts[:pdf_background] unless opts[:pdf_background].nil?
69
+ body[:pdf_landscape] = opts[:pdf_landscape] unless opts[:pdf_landscape].nil?
70
+ body[:bypass_cache] = opts[:bypass_cache] unless opts[:bypass_cache].nil?
71
+ body[:js_render] = opts[:js_render] unless opts[:js_render].nil?
72
+ body[:wait_for_selector] = opts[:wait_for_selector] unless opts[:wait_for_selector].nil?
73
+ body[:wait_until] = opts[:wait_until] unless opts[:wait_until].nil?
74
+ body[:wait_ms] = opts[:wait_ms] unless opts[:wait_ms].nil?
75
+ body[:custom_headers] = opts[:custom_headers] unless opts[:custom_headers].nil?
76
+ body[:custom_js] = opts[:custom_js] unless opts[:custom_js].nil?
77
+ body[:return_cookies] = opts[:return_cookies] unless opts[:return_cookies].nil?
78
+ body[:block_resources] = opts[:block_resources] unless opts[:block_resources].nil?
79
+ body[:timeout] = opts[:timeout] unless opts[:timeout].nil?
80
+
81
+ request('POST', '/api/scrape', body)
82
+ end
83
+
84
+ private
85
+
86
+ def request(method, path, body = {})
87
+ uri = URI.parse(@base_url + path)
88
+ http = Net::HTTP.new(uri.host, uri.port)
89
+ http.use_ssl = uri.scheme == 'https'
90
+ http.read_timeout = @timeout
91
+ http.open_timeout = 10
92
+
93
+ payload = body.to_json
94
+
95
+ req = Net::HTTP::Post.new(uri.request_uri)
96
+ req['X-API-Key'] = @api_key
97
+ req['Content-Type'] = 'application/json'
98
+ req['User-Agent'] = "resilientlink-ruby/#{VERSION}"
99
+ req['Content-Length'] = payload.bytesize.to_s
100
+ req.body = payload
101
+
102
+ response = http.request(req)
103
+ parsed = JSON.parse(response.body) rescue { 'raw' => response.body }
104
+
105
+ if response.code.to_i >= 400
106
+ message = parsed['error'] || "HTTP #{response.code}"
107
+ raise Error.new(message, status_code: response.code.to_i, body: parsed)
108
+ end
109
+
110
+ parsed
111
+ rescue Net::OpenTimeout, Net::ReadTimeout => e
112
+ raise Error.new("Request timed out: #{e.message}")
113
+ rescue SocketError, Errno::ECONNREFUSED => e
114
+ raise Error.new("Connection failed: #{e.message}")
115
+ end
116
+ end
117
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resilientlink
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Silent God Enterprise
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-05-08 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: ResilientLink delivers structured metadata from any URL with sub-second
14
+ latency — Residential proxy infrastructure, intelligent caching, and an API engineered
15
+ for enterprise scale.
16
+ email: resilientlink@silentgode.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - README.md
22
+ - lib/resilientlink.rb
23
+ homepage: https://resilientlink.silentgode.com
24
+ licenses:
25
+ - MIT
26
+ metadata: {}
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 2.6.0
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubygems_version: 3.4.19
43
+ signing_key:
44
+ specification_version: 4
45
+ summary: Official Ruby SDK for the ResilientLink Web Scraping API
46
+ test_files: []