diff --git a/lib/DDG/Goodie/UrlEscapeCodes.pm b/lib/DDG/Goodie/UrlEscapeCodes.pm
new file mode 100644
index 000000000..9282ff308
--- /dev/null
+++ b/lib/DDG/Goodie/UrlEscapeCodes.pm
@@ -0,0 +1,44 @@
+package DDG::Goodie::UrlEscapeCodes;
+# ABSTRACT: Shows URL Escape Codes
+
+use DDG::Goodie;
+use YAML::XS 'LoadFile';
+use strict;
+use warnings;
+
+zci answer_type => 'url_escape_codes';
+
+zci is_cached => 1;
+
+triggers start => 'url escape', 'url escaping', 'url escape codes', 'url escape characters',
+ 'url escape sequences';
+
+my $url_escape_codes = LoadFile(share('data.yml'));
+
+handle remainder => sub {
+ return unless $_ eq '';
+
+ return '',
+ structured_answer => {
+ id => 'url_escape_codes',
+ name => 'URL Escape Codes',
+ data => {
+ title => 'URL Escape Codes',
+ table => $url_escape_codes
+ },
+ meta => {
+ sourceName => 'december.com',
+ sourceUrl => 'http://www.december.com/html/spec/esccodes.html'
+ },
+ templates => {
+ group => 'list',
+ item => 0,
+ options => {
+ content => 'DDH.url_escape_codes.content',
+ moreAt => 1
+ }
+ }
+ };
+};
+
+1;
diff --git a/share/goodie/url_escape_codes/content.handlebars b/share/goodie/url_escape_codes/content.handlebars
new file mode 100644
index 000000000..8d151d8a9
--- /dev/null
+++ b/share/goodie/url_escape_codes/content.handlebars
@@ -0,0 +1,18 @@
+
+
+
+ {{#each table.header}}
+ {{ abbr }} |
+ {{/each}}
+
+
+
+ {{#each table.body}}
+
+ {{#each ../table.header}}
+ {{ get_value ../this this.abbr }} |
+ {{/each}}
+
+ {{/each}}
+
+
diff --git a/share/goodie/url_escape_codes/data.yml b/share/goodie/url_escape_codes/data.yml
new file mode 100644
index 000000000..bfe071aaf
--- /dev/null
+++ b/share/goodie/url_escape_codes/data.yml
@@ -0,0 +1,51 @@
+---
+header:
+ - key: Character
+ abbr: Char
+ - key: 'Escape Code'
+ abbr: 'Esc Code'
+body:
+ - Char: 'SPACE'
+ 'Esc Code': '%20'
+ - Char: '<'
+ 'Esc Code': '%3C'
+ - Char: '>'
+ 'Esc Code': '%3E'
+ - Char: '#'
+ 'Esc Code': '%23'
+ - Char: '%'
+ 'Esc Code': '%25'
+ - Char: '{'
+ 'Esc Code': '%7B'
+ - Char: '}'
+ 'Esc Code': '%7D'
+ - Char: '|'
+ 'Esc Code': '%7C'
+ - Char: '\'
+ 'Esc Code': '%5C'
+ - Char: '^'
+ 'Esc Code': '%5E'
+ - Char: '~'
+ 'Esc Code': '%7E'
+ - Char: '['
+ 'Esc Code': '%5B'
+ - Char: ']'
+ 'Esc Code': '%5D'
+ - Char: '`'
+ 'Esc Code': '%60'
+ - Char: ';'
+ 'Esc Code': '%3B'
+ - Char: '/'
+ 'Esc Code': '%2F'
+ - Char: '?'
+ 'Esc Code': '%3F'
+ - Char: ':'
+ 'Esc Code': '%3A'
+ - Char: '@'
+ 'Esc Code': '%40'
+ - Char: '='
+ 'Esc Code': '%3D'
+ - Char: '&'
+ 'Esc Code': '%26'
+ - Char: '$'
+ 'Esc Code': '%24'
\ No newline at end of file
diff --git a/share/goodie/url_escape_codes/url_escape_codes.css b/share/goodie/url_escape_codes/url_escape_codes.css
new file mode 100644
index 000000000..95232079d
--- /dev/null
+++ b/share/goodie/url_escape_codes/url_escape_codes.css
@@ -0,0 +1,3 @@
+.zci--url_escape_codes .c-list__content.chomp {
+ max-height: 14em;
+}
diff --git a/share/goodie/url_escape_codes/url_escape_codes.js b/share/goodie/url_escape_codes/url_escape_codes.js
new file mode 100644
index 000000000..6fa70d92f
--- /dev/null
+++ b/share/goodie/url_escape_codes/url_escape_codes.js
@@ -0,0 +1,14 @@
+DDH.url_escape_codes = DDH.url_escape_codes || {};
+
+DDH.url_escape_codes.build = function(ops) {
+ "use strict";
+
+ Spice.registerHelper('get_value', function(obj, key) {
+ if (obj) {
+ if (key in obj) {
+ return obj[key];
+ }
+ }
+ return '';
+ });
+};
\ No newline at end of file
diff --git a/t/UrlEscapeCodes.t b/t/UrlEscapeCodes.t
new file mode 100644
index 000000000..c48a5c4f3
--- /dev/null
+++ b/t/UrlEscapeCodes.t
@@ -0,0 +1,90 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use Test::More;
+use Test::Deep;
+use DDG::Test::Goodie;
+use YAML::XS 'LoadFile';
+
+zci answer_type => 'url_escape_codes';
+zci is_cached => 1;
+
+my $url_escape_codes = LoadFile('share/goodie/url_escape_codes/data.yml');
+
+sub build_structured_answer {
+ my $result = {
+ title => 'URL Escape Codes',
+ table => $url_escape_codes
+ };
+
+ # Check if type of header data is an Array
+ isa_ok($result->{table}->{header}, 'ARRAY');
+
+ # Check if type of body data is an Array
+ isa_ok($result->{table}->{body}, 'ARRAY');
+
+ # Check if each Header Hash has required keys
+ for (my $i = 0; $i < $#{$result->{table}->{header}}; $i++) {
+ cmp_deeply(
+ $result->{table}->{header}->[$i],
+ {
+ key => ignore(),
+ abbr => ignore()
+ }
+ );
+ }
+
+ # Check if each Body Hash has required keys
+ for (my $i = 0; $i < $#{$result->{table}->{body}}; $i++) {
+ cmp_deeply(
+ $result->{table}->{body}->[$i],
+ {
+ Char => ignore(),
+ 'Esc Code' => ignore()
+ }
+ );
+ }
+
+ # Check if title is correct or not
+ is($result->{title}, 'URL Escape Codes');
+
+ return '',
+ structured_answer => {
+ id => 'url_escape_codes',
+ name => 'URL Escape Codes',
+ data => {
+ title => 'URL Escape Codes',
+ table => $url_escape_codes
+ },
+ meta => {
+ sourceName => 'december.com',
+ sourceUrl => 'http://www.december.com/html/spec/esccodes.html'
+ },
+ templates => {
+ group => 'list',
+ item => 0,
+ options => {
+ content => 'DDH.url_escape_codes.content',
+ moreAt => 1
+ }
+ }
+ };
+}
+
+sub build_test { test_zci(build_structured_answer(@_)) }
+
+ddg_goodie_test(
+ [qw( DDG::Goodie::UrlEscapeCodes )],
+ 'url escape' => build_test(),
+ 'url escaping' => build_test(),
+ 'url escape codes' => build_test(),
+ 'url escape characters' => build_test(),
+ 'url escape sequences' => build_test(),
+ 'url encoder' => undef,
+ 'url decoder' => undef,
+ 'convert url escape' => undef,
+ 'dont run for this' => undef
+);
+
+done_testing;