uploadcare-ruby is a framework-agnostic client for the Uploadcare Upload API and REST API.
The gem is built around:
-
explicit
Uploadcare::Clientinstances -
client-scoped configuration for multi-account use
-
a small convenience layer for common workflows
-
full endpoint coverage through
client.api.restandclient.api.upload
- Ruby 3.3+
This gem is intended for plain Ruby applications and for framework integrations built on top of it.
- Use explicit
Uploadcare::Clientinstances when you need multiple accounts in one process. - Use
Uploadcare.configureandUploadcare.clientwhen one global default client is enough. - Use
client.api.restandclient.api.uploadwhen you want endpoint-level parity with the official API references.
Add the gem to your Gemfile:
gem "uploadcare-ruby"Then install:
bundleSet credentials with environment variables:
export UPLOADCARE_PUBLIC_KEY=your_public_key
export UPLOADCARE_SECRET_KEY=your_secret_keyThe gem has two public layers:
This is the default API you should use in applications:
client.filesclient.groupsclient.uploadsclient.projectclient.webhooksclient.file_metadataclient.addonsclient.conversions
This layer returns resources and collections, and it raises typed exceptions on failure.
This layer mirrors Uploadcare’s REST and Upload APIs:
client.api.restclient.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.
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_urlYou 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)
endThe recommended style is explicit Uploadcare::Client instances. Global configuration is best treated as a default.
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_urlUse 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
endOr 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:
account_a = Uploadcare::Client.new(config: base_config.with(public_key: "pk-a", secret_key: "sk-a"))
account_b = Uploadcare::Client.new(config: base_config.with(public_key: "pk-b", secret_key: "sk-b"))Common configuration options:
public_keysecret_keyauth_typemultipart_size_thresholdmultipart_chunk_sizeupload_threadsupload_timeoutmax_upload_retriessign_uploadsupload_signature_lifetimeuse_subdomainscdn_base_postfixdefault_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_baseThe 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.
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)file = File.open("photo.jpg", "rb") do |io|
client.files.upload(io, store: true, metadata: { subsystem: "avatars" })
endfiles = [
File.open("photo-1.jpg", "rb"),
File.open("photo-2.jpg", "rb")
]
uploaded = client.uploads.upload(files, store: true)
files.each(&:close)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 secondspoll_max_interval(default:10) maximum polling interval in secondspoll_timeout(default:300) maximum total polling time in seconds
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
endYou 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)
endCommon upload options:
store: true | false | "auto"metadata: { key: value }signature: "..."expire: unix_timestampasync: truefor URL uploadsthreads:andpart_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)
endfile = client.files.find(uuid: "file-uuid")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.allFilters and API parameters can still be passed through:
files = client.files.list(stored: true, removed: false, limit: 100)file.store
file.delete
file.reload
file.reload(params: { include: "appdata" })result = client.files.batch_store(uuids: ["uuid-1", "uuid-2"])
puts result.status
puts result.result.map(&:uuid)
puts result.problemsThe same shape applies to client.files.batch_delete.
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")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.deleteUseful group helpers:
group.cdn_url
group.file_cdn_urlsFetch the current project:
project = client.project.current
puts project.name
puts project.pub_key
puts project.collaboratorsclient.file_metadata.update(uuid: file.uuid, key: "category", value: "avatar")
client.file_metadata.show(uuid: file.uuid, key: "category")
client.file_metadata.index(uuid: file.uuid)
client.file_metadata.delete(uuid: file.uuid, key: "category")Uploadcare::FileMetadata is also available as a resource if you need to hold metadata state locally.
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)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.
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.
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)The convenience layer raises exceptions:
Uploadcare::Exception::RequestErrorUploadcare::Exception::InvalidRequestErrorUploadcare::Exception::NotFoundErrorUploadcare::Exception::UploadErrorUploadcare::Exception::MultipartUploadErrorUploadcare::Exception::UploadTimeoutErrorUploadcare::Exception::ThrottleError
Example:
begin
client.files.find(uuid: "missing")
rescue Uploadcare::Exception::NotFoundError => e
warn e.message
endThe 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
endMost 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.
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.listUpload 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.
- api_examples/README.md: one canonical script per documented REST and Upload API endpoint
- examples/README.md: workflow-oriented demos built on the public client API
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.jpegSee: