Blog

How to migrate your websites from UptimeRobot to StatusCake with Ruby

UptimeRobot, which I have been a happy user of since 2010, has been failing me in the last months, reporting hundreds of false downtime alerts for my 34 websites monitored by them. Since beginning of March it has been particularly bad with 20-30 false alerts per day.

I tried to get in touch via Twitter and email a couple of times but received no response so it was time to say goodbye.

Move on

I evaluated the alternatives to UptimeRobot and found StatusCake to be a good option. Generous free plan, good looking service, targeting users coming from UptimeRobot... :)

Since I wasn't gonna copy and paste all my monitors to StatusCake manually, and both UptimeRobot and StatusCake have nice APIs, I threw together a quick Ruby script to do the migration for me.

Without further ado: (see comments below code before running it)

require 'net/http'
require 'open-uri'
require 'json'

# Enter your credentials for UptimeRobot and StatusCake here:
ur_api_key  = 'YOUR UPTIMEROBOT API KEY'
sk_api_key  = 'YOUR STATUSCAKE API KEY'
sk_username = 'YOUR STATUSCAKE USERNAME'

sk_uri = URI.parse('https://www.statuscake.com')
ur_url = "http://api.uptimerobot.com/getMonitors?noJsonCallback=1&format=json&apiKey=#{ur_api_key}"

def json_from_sk_request(request)
  request['API']      = sk_api_key
  request['Username'] = sk_username
  response = Net::HTTP.start(sk_uri.hostname, sk_uri.port, use_ssl: sk_uri.scheme == 'https') do |http|
    http.request(request)
  end
  reponse_json = JSON.load(response.body)
end

# Load monitors from UptimeRobot
json = JSON.load(open(ur_url))

# Load StatusCake contact group
# Create one before running this script:
# https://www.statuscake.com/App/ContactGroup.php
request = Net::HTTP::Get.new('/API/ContactGroups')
reponse_json = json_from_sk_request(request)
raise 'Create a StatusCake contact group first.' if reponse_json.empty?
sk_contact_group_id = reponse_json.first['ContactID']

# Create monitors in StatusCake
monitors = json['monitors']['monitor']
monitors.each do |monitor|
  name, url = monitor['friendlyname'], monitor['url']
  puts "Creating monitor: #{name} - #{url}"
  request = Net::HTTP::Put.new('/API/Tests/Update')
  request.set_form_data(
    'WebsiteName'  => name,
    'WebsiteURL'   => url,
    'TestType'     => 'HTTP',
    'CheckRate'    => 300,
    'ContactGroup' => sk_contact_group_id
  )
  reponse_json = json_from_sk_request(request)
  if reponse_json['Success']
    puts 'Test created successfully.'
  else
    puts "Error creating monitor: #{reponse_json}"
  end
end

Comments:

  • Fill in your UptimeRobot API key and StatusCake API key and username where it says in the script.
  • Create a StatusCake contact group before running the script. If you have multiple contact groups, the script will associate all monitors with the first one.
  • If you want to create your StatusCake monitors with additional configuration, update the request.set_form_data part.

Clean up

Once you have verified that the monitors have been created successfully in StatusCake, use this script to delete them from UptimeRobot:

require 'net/http'
require 'open-uri'
require 'json'

# Enter your UptimeRobot API key:
ur_api_key = 'YOUR UPTIMEROBOT API KEY'

ur_url = "http://api.uptimerobot.com/getMonitors?noJsonCallback=1&format=json&apiKey=#{ur_api_key}"

# Load monitors from UptimeRobot
json = JSON.load(open(ur_url))

# Delete monitors from UptimeRobot
monitors = json['monitors']['monitor']
monitors.each do |monitor|
  monitor_id = monitor['id']
  puts "Deleting monitor #{monitor_id}"
  url = "http://api.uptimerobot.com/deleteMonitor?noJsonCallback=1&format=json&apiKey=#{ur_api_key}&monitorID=#{monitor_id}"
  reponse_json = JSON.load(open(url))
  if reponse_json['stat'] == 'ok'
    puts 'Test created successfully.'
  else
    puts "Error creating monitor: #{reponse_json}"
  end
end

(notice how the UptimeRobot API let's you carelessly delete monitors via GET requests? Big no-no!)

Discuss this post on Hacker News

Ideas? Constructive criticism? Think I'm stupid? Let me know in the comments!