# Rails (/es/quickstarts/rails)

<!-- agent-signals: reading_time_min: 4 · est_tokens: 2191 · updated: 2026-07-30 -->
Related: [Node.js](/es/quickstarts/nodejs.md), [Next.js](/es/quickstarts/nextjs.md), [Express](/es/quickstarts/express.md), [NestJS](/es/quickstarts/nestjs.md), [Fastify](/es/quickstarts/fastify.md), [Hono](/es/quickstarts/hono.md)

<div id="prerequisites">
  ## Requisitos previos [#requisitos-previos]
</div>

* Ruby 3.0+ y Rails 7+
* Una clave de API de Firecrawl: [consigue una gratis](https://www.firecrawl.dev/app/api-keys)

<div id="configuration">
  ## Configuración [#configuración]
</div>

Agrega tu clave de API a las credenciales de Rails o al entorno:

```bash
export FIRECRAWL_API_KEY=fc-YOUR-API-KEY
```

<div id="create-a-service">
  ## Crear un servicio [#crear-un-servicio]
</div>

Crea `app/services/firecrawl_service.rb`:

```ruby
require "net/http"
require "json"
require "uri"

class FirecrawlService
  BASE_URL = "https://api.firecrawl.dev/v2"

  def initialize(api_key: ENV.fetch("FIRECRAWL_API_KEY"))
    @api_key = api_key
  end

  def search(query, limit: 5)
    post("/search", { query: query, limit: limit })
  end

  def scrape(url, **options)
    post("/scrape", { url: url }.merge(options))
  end

  def interact(url, prompt, follow_up: nil)
    # 1. Hacer scraping para abrir una sesión de navegador
    scrape_result = scrape(url, formats: ["markdown"])
    scrape_id = scrape_result.dig("data", "metadata", "scrapeId")

    # 2. Enviar el primer prompt
    post("/scrape/#{scrape_id}/interact", { prompt: prompt })

    # 3. Enviar el prompt de seguimiento
    result = nil
    if follow_up
      result = post("/scrape/#{scrape_id}/interact", { prompt: follow_up })
    end

    # 4. Cerrar la sesión
    delete("/scrape/#{scrape_id}/interact")

    result || scrape_result
  end

  private

  def post(endpoint, payload)
    uri = URI("#{BASE_URL}#{endpoint}")
    request = Net::HTTP::Post.new(uri)
    request["Authorization"] = "Bearer #{@api_key}"
    request["Content-Type"] = "application/json"
    request.body = payload.to_json

    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end

    JSON.parse(response.body)
  end

  def delete(endpoint)
    uri = URI("#{BASE_URL}#{endpoint}")
    request = Net::HTTP::Delete.new(uri)
    request["Authorization"] = "Bearer #{@api_key}"

    Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end
  end
end
```

<div id="create-a-controller">
  ## Crear un controlador [#crear-un-controlador]
</div>

Genera un controlador:

```bash
rails generate controller Firecrawl search scrape interact --skip-routes
```

Edita `app/controllers/firecrawl_controller.rb`:

```ruby
class FirecrawlController < ApplicationController
  skip_before_action :verify_authenticity_token

  def search
    service = FirecrawlService.new
    result = service.search(params.require(:query), limit: params.fetch(:limit, 5).to_i)
    render json: result
  end

  def scrape
    service = FirecrawlService.new
    result = service.scrape(params.require(:url))
    render json: result
  end

  def interact
    service = FirecrawlService.new
    result = service.interact(
      params.require(:url),
      params.require(:prompt),
      follow_up: params[:followUp]
    )
    render json: result
  end
end
```

<div id="add-routes">
  ## Añadir rutas [#añadir-rutas]
</div>

En `config/routes.rb`:

```ruby
Rails.application.routes.draw do
  post "api/search", to: "firecrawl#search"
  post "api/scrape", to: "firecrawl#scrape"
  post "api/interact", to: "firecrawl#interact"
end
```

<div id="test-it">
  ## Pruébalo [#pruébalo]
</div>

```bash
rails server

# Buscar en la web
curl -X POST http://localhost:3000/api/search \
  -H "Content-Type: application/json" \
  -d '{"query": "firecrawl web scraping"}'

# Hacer scraping de una página
curl -X POST http://localhost:3000/api/scrape \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}'

# Interactuar con una página
curl -X POST http://localhost:3000/api/interact \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.amazon.com", "prompt": "Search for iPhone 16 Pro Max", "followUp": "Click on the first result and tell me the price"}'
```

<div id="next-steps">
  ## Próximos pasos [#próximos-pasos]
</div>

<CardGroup cols="2">
  <Card title="Documentación de Search" icon="<svg xmlns=&#x22;http://www.w3.org/2000/svg&#x22; viewBox=&#x22;0 0 24 24&#x22; fill=&#x22;none&#x22;><path d=&#x22;M8 7L16 7&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M8 11L12 11&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M13 21.5V21C13 18.1716 13 16.7574 13.8787 15.8787C14.7574 15 16.1716 15 19 15H19.5M20 13.3431V10C20 6.22876 20 4.34315 18.8284 3.17157C17.6569 2 15.7712 2 12 2C8.22877 2 6.34315 2 5.17157 3.17157C4 4.34314 4 6.22876 4 10L4 14.5442C4 17.7892 4 19.4117 4.88607 20.5107C5.06508 20.7327 5.26731 20.9349 5.48933 21.1139C6.58831 22 8.21082 22 11.4558 22C12.1614 22 12.5141 22 12.8372 21.886C12.9044 21.8623 12.9702 21.835 13.0345 21.8043C13.3436 21.6564 13.593 21.407 14.0919 20.9081L18.8284 16.1716C19.4065 15.5935 19.6955 15.3045 19.8478 14.9369C20 14.5694 20 14.1606 20 13.3431Z&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/></svg>" href="/es/features/search">
    Buscar en la web y obtener el contenido completo de la página
  </Card>

  <Card title="Documentación de scraping" icon="<svg xmlns=&#x22;http://www.w3.org/2000/svg&#x22; viewBox=&#x22;0 0 24 24&#x22; fill=&#x22;none&#x22;><path d=&#x22;M8 7L16 7&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M8 11L12 11&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M13 21.5V21C13 18.1716 13 16.7574 13.8787 15.8787C14.7574 15 16.1716 15 19 15H19.5M20 13.3431V10C20 6.22876 20 4.34315 18.8284 3.17157C17.6569 2 15.7712 2 12 2C8.22877 2 6.34315 2 5.17157 3.17157C4 4.34314 4 6.22876 4 10L4 14.5442C4 17.7892 4 19.4117 4.88607 20.5107C5.06508 20.7327 5.26731 20.9349 5.48933 21.1139C6.58831 22 8.21082 22 11.4558 22C12.1614 22 12.5141 22 12.8372 21.886C12.9044 21.8623 12.9702 21.835 13.0345 21.8043C13.3436 21.6564 13.593 21.407 14.0919 20.9081L18.8284 16.1716C19.4065 15.5935 19.6955 15.3045 19.8478 14.9369C20 14.5694 20 14.1606 20 13.3431Z&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/></svg>" href="/es/features/scrape">
    Todas las opciones de scraping, incluidos formatos, acciones y proxies
  </Card>

  <Card title="Documentación de Interact" icon="<svg xmlns=&#x22;http://www.w3.org/2000/svg&#x22; viewBox=&#x22;0 0 24 24&#x22; fill=&#x22;none&#x22;><path d=&#x22;M8 7L16 7&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M8 11L12 11&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M13 21.5V21C13 18.1716 13 16.7574 13.8787 15.8787C14.7574 15 16.1716 15 19 15H19.5M20 13.3431V10C20 6.22876 20 4.34315 18.8284 3.17157C17.6569 2 15.7712 2 12 2C8.22877 2 6.34315 2 5.17157 3.17157C4 4.34314 4 6.22876 4 10L4 14.5442C4 17.7892 4 19.4117 4.88607 20.5107C5.06508 20.7327 5.26731 20.9349 5.48933 21.1139C6.58831 22 8.21082 22 11.4558 22C12.1614 22 12.5141 22 12.8372 21.886C12.9044 21.8623 12.9702 21.835 13.0345 21.8043C13.3436 21.6564 13.593 21.407 14.0919 20.9081L18.8284 16.1716C19.4065 15.5935 19.6955 15.3045 19.8478 14.9369C20 14.5694 20 14.1606 20 13.3431Z&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/></svg>" href="/es/features/interact">
    Haz clic, completa formularios y extrae contenido dinámico
  </Card>

  <Card title="Referencia de la API" icon="<svg xmlns=&#x22;http://www.w3.org/2000/svg&#x22; viewBox=&#x22;0 0 24 24&#x22; fill=&#x22;none&#x22;><path d=&#x22;M16 6.99998L19.0664 9.64296C20.3554 10.7541 21 11.3096 21 12C21 12.6903 20.3555 13.2459 19.0664 14.357L16 17&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M8 6.99998L4.93365 9.64296C3.64455 10.7541 3 11.3096 3 12C3 12.6903 3.64455 13.2459 4.93365 14.357L8 17&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/></svg>" href="/es/api-reference/v2-introduction">
    Documentación completa de la API REST
  </Card>
</CardGroup>
