Uploadcare Ruby SDK

license Build Status Uploadcare stack on StackShare

uploadcare-ruby is a framework-agnostic client for the Uploadcare Upload API and REST API.

The gem is built around:

Requirements

  • Ruby 3.3+

Compatibility

This gem is intended for plain Ruby applications and for framework integrations built on top of it.

  • Use explicit Uploadcare::Client instances when you need multiple accounts in one process.
  • Use Uploadcare.configure and Uploadcare.client when one global default client is enough.
  • Use client.api.rest and client.api.upload when you want endpoint-level parity with the official API references.

Installation

Add the gem to your Gemfile:

gem "uploadcare-ruby"

Then install:

bundle

Set credentials with environment variables:

export UPLOADCARE_PUBLIC_KEY=your_public_key
export UPLOADCARE_SECRET_KEY=your_secret_key

Design

The gem has two public layers:

Convenience layer

This is the default API you should use in applications:

  • client.files
  • client.groups
  • client.uploads
  • client.project
  • client.webhooks
  • client.file_metadata
  • client.addons
  • client.conversions

This layer returns resources and collections, and it raises typed exceptions on failure.

Raw parity layer

This layer mirrors Uploadcare’s REST and Upload APIs:

  • client.api.rest
  • client.api.upload

This layer returns Uploadcare::Result objects so you can inspect success and failure explicitly.

That split keeps app code clean without losing full API coverage.

Quick Start

require "uploadcare"

client = Uploadcare::Client.new(
  public_key: ENV.fetch("UPLOADCARE_PUBLIC_KEY"),
  secret_key: ENV.fetch("UPLOADCARE_SECRET_KEY")
)

file = File.open("photo.jpg", "rb") do |io|
  client.files.upload(io, store: true)
end

puts file.uuid
puts file.cdn_url

You can also configure a default global client:

Uploadcare.configure do |config|
  config.public_key = ENV.fetch("UPLOADCARE_PUBLIC_KEY")
  config.secret_key = ENV.fetch("UPLOADCARE_SECRET_KEY")
end

file = File.open("photo.jpg", "rb") do |io|
  Uploadcare.files.upload(io, store: true)
end

The recommended style is explicit Uploadcare::Client instances. Global configuration is best treated as a default.

Example Usage

This is the shortest end-to-end flow for the main public API:

require "uploadcare"

client = Uploadcare::Client.new(
  public_key: ENV.fetch("UPLOADCARE_PUBLIC_KEY"),
  secret_key: ENV.fetch("UPLOADCARE_SECRET_KEY")
)

file = File.open("photo.jpg", "rb") do |io|
  client.files.upload(io, store: true)
end

group = client.groups.create(uuids: [file.uuid])

puts file.uuid
puts file.cdn_url
puts group.id
puts group.cdn_url

Configuration

Use Uploadcare.configure to set process-wide defaults:

Uploadcare.configure do |config|
  config.public_key = "public_key"
  config.secret_key = "secret_key"
  config.auth_type = "Uploadcare"
  config.use_subdomains = false
end

Or build configuration objects directly:

base_config = Uploadcare::Configuration.new(
  public_key: "public_key",
  secret_key: "secret_key"
)

client = Uploadcare::Client.new(config: base_config)

Configuration objects are copyable:

 = Uploadcare::Client.new(config: base_config.with(public_key: "pk-a", secret_key: "sk-a"))
 = Uploadcare::Client.new(config: base_config.with(public_key: "pk-b", secret_key: "sk-b"))

Common configuration options:

  • public_key
  • secret_key
  • auth_type
  • multipart_size_threshold
  • multipart_chunk_size
  • upload_threads
  • upload_timeout
  • max_upload_retries
  • sign_uploads
  • upload_signature_lifetime
  • use_subdomains
  • cdn_base_postfix
  • default_cdn_base

CDN helpers:

Uploadcare.configure do |config|
  config.use_subdomains = true
  config.cdn_base_postfix = "https://ucarecd.net/"
  config.default_cdn_base = "https://ucarecdn.com/"
end

Uploadcare.configuration.custom_cname
Uploadcare.configuration.cdn_base

Multi-Account Usage

The gem is designed to support multiple Uploadcare projects in the same process:

primary = Uploadcare::Client.new(public_key: "pk-1", secret_key: "sk-1")
secondary = Uploadcare::Client.new(public_key: "pk-2", secret_key: "sk-2")

primary_file = primary.files.find(uuid: "uuid-1")
secondary_file = secondary.files.find(uuid: "uuid-2")

You can also derive temporary variants from an existing client:

admin_client = primary.with(secret_key: "different-secret")

Resource objects retain their client context, so subsequent instance operations stay bound to the correct account.

Uploads

Smart upload

client.uploads.upload accepts:

  • an IO or file object
  • an array of IO or file objects
  • an HTTP or HTTPS URL string
file = File.open("photo.jpg", "rb") do |io|
  client.uploads.upload(io, store: true)
end

remote_file = client.uploads.upload("https://example.com/image.jpg", store: true)

Single file upload

file = File.open("photo.jpg", "rb") do |io|
  client.files.upload(io, store: true, metadata: { subsystem: "avatars" })
end

Multiple file upload

files = [
  File.open("photo-1.jpg", "rb"),
  File.open("photo-2.jpg", "rb")
]

uploaded = client.uploads.upload(files, store: true)

files.each(&:close)

Upload from URL

Synchronous:

file = client.files.upload_from_url("https://example.com/image.jpg", store: true)

Async:

job = client.uploads.upload_from_url(url: "https://example.com/image.jpg", async: true, store: true)
status = client.uploads.upload_from_url_status(token: job.fetch("token"))

When async mode is enabled, the convenience layer returns the raw status token hash because the file does not exist yet.

Polling options for synchronous URL uploads:

  • poll_interval (default: 1) initial status polling interval in seconds
  • poll_max_interval (default: 10) maximum polling interval in seconds
  • poll_timeout (default: 300) maximum total polling time in seconds

Multipart upload with progress

File.open("large-video.mp4", "rb") do |io|
  file = client.uploads.multipart_upload(file: io, store: true, threads: 4) do |progress|
    uploaded = progress[:uploaded]
    total = progress[:total]
    puts "#{uploaded}/#{total}"
  end

  puts file.uuid
end

Signed uploads

You can enable signed uploads globally:

client = Uploadcare::Client.new(
  public_key: "public",
  secret_key: "secret",
  sign_uploads: true
)

Or pass explicit signature data per request:

File.open("photo.jpg", "rb") do |io|
  client.files.upload(io, signature: "signature", expire: 1_900_000_000)
end

Upload options

Common upload options:

  • store: true | false | "auto"
  • metadata: { key: value }
  • signature: "..."
  • expire: unix_timestamp
  • async: true for URL uploads
  • threads: and part_size: for multipart uploads

If you prefer the older top-level style, the same flows can still be written through the global client:

Uploadcare.configure do |config|
  config.public_key = ENV.fetch("UPLOADCARE_PUBLIC_KEY")
  config.secret_key = ENV.fetch("UPLOADCARE_SECRET_KEY")
end

file = File.open("photo.jpg", "rb") do |io|
  Uploadcare.files.upload(io, store: true)
end

Files

Find a file

file = client.files.find(uuid: "file-uuid")

List files

files = client.files.list(limit: 100)
files.each { |file| puts file.uuid }

List responses are Uploadcare::Collections::Paginated:

files.next_page
files.previous_page
files.all

Filters and API parameters can still be passed through:

files = client.files.list(stored: true, removed: false, limit: 100)

Resource operations

file.store
file.delete
file.reload
file.reload(params: { include: "appdata" })

Batch operations

result = client.files.batch_store(uuids: ["uuid-1", "uuid-2"])

puts result.status
puts result.result.map(&:uuid)
puts result.problems

The same shape applies to client.files.batch_delete.

Copy operations

copied = client.files.copy_to_local(source: file.uuid, options: { store: true })
remote_url = client.files.copy_to_remote(source: file.uuid, target: "custom_storage")

Instance-level variants are also available:

copied = file.copy_to_local(options: { store: true })
remote_url = file.copy_to_remote(target: "custom_storage")

Groups

Create a group:

group = client.groups.create(uuids: ["uuid-1", "uuid-2"])

Find and list groups:

group = client.groups.find(group_id: "group-uuid~2")
groups = client.groups.list(limit: 50)

Delete a group:

group.delete

Useful group helpers:

group.cdn_url
group.file_cdn_urls

Project

Fetch the current project:

project = client.project.current

puts project.name
puts project.pub_key
puts project.collaborators

Metadata

client..update(uuid: file.uuid, key: "category", value: "avatar")
client..show(uuid: file.uuid, key: "category")
client..index(uuid: file.uuid)
client..delete(uuid: file.uuid, key: "category")

Uploadcare::FileMetadata is also available as a resource if you need to hold metadata state locally.

Webhooks

webhook = client.webhooks.create(
  target_url: "https://example.com/uploadcare",
  event: "file.uploaded",
  is_active: true
)

client.webhooks.list
client.webhooks.update(id: webhook.id, is_active: false)
client.webhooks.delete(target_url: webhook.target_url)

Add-ons

execution = client.addons.aws_rekognition_detect_labels(uuid: file.uuid)
client.addons.aws_rekognition_detect_labels_status(request_id: execution.request_id)

scan = client.addons.uc_clamav_virus_scan(uuid: file.uuid)
client.addons.uc_clamav_virus_scan_status(request_id: scan.request_id)

background = client.addons.remove_bg(uuid: file.uuid)
client.addons.remove_bg_status(request_id: background.request_id)

These methods return Uploadcare::AddonExecution resources.

Conversions

Document conversions:

info = client.conversions.documents.info(uuid: file.uuid)
job = client.conversions.documents.convert(uuid: file.uuid, format: :pdf)
status = client.conversions.documents.status(token: job.fetch("result").first.fetch("token"))

Video conversions:

job = client.conversions.videos.convert(uuid: file.uuid, format: :webm, quality: :normal)
status = client.conversions.videos.status(token: job.result.first.fetch("token"))

Document conversion convert returns the API response hash.

Video conversion convert returns a Uploadcare::VideoConversion resource.

Secure Delivery

The gem includes signed URL generators for delivery workflows.

generator = Uploadcare::SignedUrlGenerators::AkamaiGenerator.new(
  cdn_host: "example.com",
  secret_key: "your_hex_encoded_akamai_secret"
)

signed_url = generator.generate_url("a7d5645e-5cd7-4046-819f-a6a2933bafe3")

Custom ACL and wildcard examples:

generator.generate_url("a7d5645e-5cd7-4046-819f-a6a2933bafe3", "/*")
generator.generate_url("a7d5645e-5cd7-4046-819f-a6a2933bafe3", wildcard: true)

Errors and Results

The convenience layer raises exceptions:

  • Uploadcare::Exception::RequestError
  • Uploadcare::Exception::InvalidRequestError
  • Uploadcare::Exception::NotFoundError
  • Uploadcare::Exception::UploadError
  • Uploadcare::Exception::MultipartUploadError
  • Uploadcare::Exception::UploadTimeoutError
  • Uploadcare::Exception::ThrottleError

Example:

begin
  client.files.find(uuid: "missing")
rescue Uploadcare::Exception::NotFoundError => e
  warn e.message
end

The raw API layer returns Uploadcare::Result:

result = client.api.rest.files.info(uuid: "file-uuid")

if result.success?
  puts result.success
else
  warn result.error_message
end

Request Options

Most API calls accept request_options: and pass them to the HTTP layer.

Example:

client.files.find(uuid: "file-uuid", request_options: { timeout: 10 })

Use this when you need per-request timeout control without changing the client’s default configuration.

Raw API Access

The gem exposes full endpoint-level access through client.api.

REST API:

client.api.rest.files.list(params: { limit: 10 })
client.api.rest.files.info(uuid: "file-uuid")
client.api.rest.project.show
client.api.rest.webhooks.list

Upload API:

File.open("photo.jpg", "rb") do |io|
  client.api.upload.files.direct(file: io, store: true)
end

client.api.upload.files.from_url(source_url: "https://example.com/image.jpg", async: true)
client.api.upload.groups.create(files: ["uuid-1", "uuid-2"])

Use this layer when you want exact control over the documented endpoints or when you are wrapping the gem from another library.

The raw layer is part of the public surface, but it is intentionally less promoted than client.files, client.groups, and the other convenience accessors.

Examples

Run examples with project-managed Ruby:

mise exec -- ruby api_examples/rest_api/get_project.rb
mise exec -- ruby examples/simple_upload.rb spec/fixtures/kitten.jpeg

Upgrading from v4.x

See: