diff --git a/homeassistant/components/calendar/__init__.py b/homeassistant/components/calendar/__init__.py
index 5198381b9767a3e537be444c6d1bd8ca9aa19dcf..f5e1d581891a4db19d6622e9d37ad71211b801be 100644
--- a/homeassistant/components/calendar/__init__.py
+++ b/homeassistant/components/calendar/__init__.py
@@ -9,6 +9,8 @@ import logging
 from datetime import timedelta
 import re
 
+from aiohttp import web
+
 from homeassistant.components.google import (
     CONF_OFFSET, CONF_DEVICE_ID, CONF_NAME)
 from homeassistant.const import STATE_OFF, STATE_ON
@@ -18,11 +20,15 @@ from homeassistant.helpers.entity import Entity, generate_entity_id
 from homeassistant.helpers.entity_component import EntityComponent
 from homeassistant.helpers.template import DATE_STR_FORMAT
 from homeassistant.util import dt
+from homeassistant.components import http
+
 
 _LOGGER = logging.getLogger(__name__)
 
 DOMAIN = 'calendar'
 
+DEPENDENCIES = ['http']
+
 ENTITY_ID_FORMAT = DOMAIN + '.{}'
 
 SCAN_INTERVAL = timedelta(seconds=60)
@@ -34,6 +40,8 @@ def async_setup(hass, config):
     component = EntityComponent(
         _LOGGER, DOMAIN, hass, SCAN_INTERVAL, DOMAIN)
 
+    hass.http.register_view(CalendarEventView(component))
+
     yield from component.async_setup(config)
     return True
 
@@ -42,6 +50,14 @@ DEFAULT_CONF_TRACK_NEW = True
 DEFAULT_CONF_OFFSET = '!!'
 
 
+def get_date(date):
+    """Get the dateTime from date or dateTime as a local."""
+    if 'date' in date:
+        return dt.start_of_local_day(dt.dt.datetime.combine(
+            dt.parse_date(date['date']), dt.dt.time.min))
+    return dt.as_local(dt.parse_datetime(date['dateTime']))
+
+
 # pylint: disable=too-many-instance-attributes
 class CalendarEventDevice(Entity):
     """A calendar event device."""
@@ -144,15 +160,8 @@ class CalendarEventDevice(Entity):
             self.cleanup()
             return
 
-        def _get_date(date):
-            """Get the dateTime from date or dateTime as a local."""
-            if 'date' in date:
-                return dt.start_of_local_day(dt.dt.datetime.combine(
-                    dt.parse_date(date['date']), dt.dt.time.min))
-            return dt.as_local(dt.parse_datetime(date['dateTime']))
-
-        start = _get_date(self.data.event['start'])
-        end = _get_date(self.data.event['end'])
+        start = get_date(self.data.event['start'])
+        end = get_date(self.data.event['end'])
 
         summary = self.data.event.get('summary', '')
 
@@ -176,10 +185,37 @@ class CalendarEventDevice(Entity):
 
         # cleanup the string so we don't have a bunch of double+ spaces
         self._cal_data['message'] = re.sub('  +', '', summary).strip()
-
         self._cal_data['offset_time'] = offset_time
         self._cal_data['location'] = self.data.event.get('location', '')
         self._cal_data['description'] = self.data.event.get('description', '')
         self._cal_data['start'] = start
         self._cal_data['end'] = end
         self._cal_data['all_day'] = 'date' in self.data.event['start']
+
+
+class CalendarEventView(http.HomeAssistantView):
+    """View to retrieve calendar content."""
+
+    url = '/api/calendar/{entity_id}'
+    name = 'api:calendar'
+
+    def __init__(self, component):
+        """Initialize calendar view."""
+        self.component = component
+
+    async def get(self, request, entity_id):
+        """Return calendar events."""
+        entity = self.component.get_entity('calendar.' + entity_id)
+        start = request.query.get('start')
+        end = request.query.get('end')
+        if None in (start, end, entity):
+            return web.Response(status=400)
+        try:
+            start_date = dt.parse_datetime(start)
+            end_date = dt.parse_datetime(end)
+        except (ValueError, AttributeError):
+            return web.Response(status=400)
+        event_list = await entity.async_get_events(request.app['hass'],
+                                                   start_date,
+                                                   end_date)
+        return self.json(event_list)
diff --git a/homeassistant/components/calendar/caldav.py b/homeassistant/components/calendar/caldav.py
index 6f92891c551d769dc7284cde9024c4087df71aee..9c30d1481f8af941bec2d76d32ef805bf6908204 100644
--- a/homeassistant/components/calendar/caldav.py
+++ b/homeassistant/components/calendar/caldav.py
@@ -11,7 +11,7 @@ import re
 import voluptuous as vol
 
 from homeassistant.components.calendar import (
-    PLATFORM_SCHEMA, CalendarEventDevice)
+    PLATFORM_SCHEMA, CalendarEventDevice, get_date)
 from homeassistant.const import (
     CONF_NAME, CONF_PASSWORD, CONF_URL, CONF_USERNAME)
 import homeassistant.helpers.config_validation as cv
@@ -92,7 +92,7 @@ def setup_platform(hass, config, add_devices, disc_info=None):
         if not config.get(CONF_CUSTOM_CALENDARS):
             device_data = {
                 CONF_NAME: calendar.name,
-                CONF_DEVICE_ID: calendar.name
+                CONF_DEVICE_ID: calendar.name,
             }
             calendar_devices.append(
                 WebDavCalendarEventDevice(hass, device_data, calendar)
@@ -120,6 +120,10 @@ class WebDavCalendarEventDevice(CalendarEventDevice):
         attributes = super().device_state_attributes
         return attributes
 
+    async def async_get_events(self, hass, start_date, end_date):
+        """Get all events in a specific time frame."""
+        return await self.data.async_get_events(hass, start_date, end_date)
+
 
 class WebDavCalendarData(object):
     """Class to utilize the calendar dav client object to get next event."""
@@ -131,6 +135,33 @@ class WebDavCalendarData(object):
         self.search = search
         self.event = None
 
+    async def async_get_events(self, hass, start_date, end_date):
+        """Get all events in a specific time frame."""
+        # Get event list from the current calendar
+        vevent_list = await hass.async_add_job(self.calendar.date_search,
+                                               start_date, end_date)
+        event_list = []
+        for event in vevent_list:
+            vevent = event.instance.vevent
+            uid = None
+            if hasattr(vevent, 'uid'):
+                uid = vevent.uid.value
+            data = {
+                "uid": uid,
+                "title": vevent.summary.value,
+                "start": self.get_hass_date(vevent.dtstart.value),
+                "end": self.get_hass_date(self.get_end_date(vevent)),
+                "location": self.get_attr_value(vevent, "location"),
+                "description": self.get_attr_value(vevent, "description"),
+            }
+
+            data['start'] = get_date(data['start']).isoformat()
+            data['end'] = get_date(data['end']).isoformat()
+
+            event_list.append(data)
+
+        return event_list
+
     @Throttle(MIN_TIME_BETWEEN_UPDATES)
     def update(self):
         """Get the latest data."""
diff --git a/homeassistant/components/calendar/demo.py b/homeassistant/components/calendar/demo.py
index 7823f03c85ecf476c2c76e7e74c1a9ce93289a54..5ddd9fe8e3d9393daae1e746556d29b21e8a6203 100644
--- a/homeassistant/components/calendar/demo.py
+++ b/homeassistant/components/calendar/demo.py
@@ -4,8 +4,10 @@ Demo platform that has two fake binary sensors.
 For more details about this platform, please refer to the documentation
 https://home-assistant.io/components/demo/
 """
+import copy
+
 import homeassistant.util.dt as dt_util
-from homeassistant.components.calendar import CalendarEventDevice
+from homeassistant.components.calendar import CalendarEventDevice, get_date
 from homeassistant.components.google import CONF_DEVICE_ID, CONF_NAME
 
 
@@ -16,12 +18,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
     add_devices([
         DemoGoogleCalendar(hass, calendar_data_future, {
             CONF_NAME: 'Future Event',
-            CONF_DEVICE_ID: 'future_event',
+            CONF_DEVICE_ID: 'calendar_1',
         }),
 
         DemoGoogleCalendar(hass, calendar_data_current, {
             CONF_NAME: 'Current Event',
-            CONF_DEVICE_ID: 'current_event',
+            CONF_DEVICE_ID: 'calendar_2',
         }),
     ])
 
@@ -29,11 +31,21 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
 class DemoGoogleCalendarData(object):
     """Representation of a Demo Calendar element."""
 
+    event = {}
+
     # pylint: disable=no-self-use
     def update(self):
         """Return true so entity knows we have new data."""
         return True
 
+    async def async_get_events(self, hass, start_date, end_date):
+        """Get all events in a specific time frame."""
+        event = copy.copy(self.event)
+        event['title'] = event['summary']
+        event['start'] = get_date(event['start']).isoformat()
+        event['end'] = get_date(event['end']).isoformat()
+        return [event]
+
 
 class DemoGoogleCalendarDataFuture(DemoGoogleCalendarData):
     """Representation of a Demo Calendar for a future event."""
@@ -80,3 +92,7 @@ class DemoGoogleCalendar(CalendarEventDevice):
         """Initialize Google Calendar but without the API calls."""
         self.data = calendar_data
         super().__init__(hass, data)
+
+    async def async_get_events(self, hass, start_date, end_date):
+        """Get all events in a specific time frame."""
+        return await self.data.async_get_events(hass, start_date, end_date)
diff --git a/homeassistant/components/calendar/google.py b/homeassistant/components/calendar/google.py
index 6c26c65ebe77fd3ccb1c1719534b0c209dc93b8b..da76530a36d634c2cb2e0291f1467502aea85276 100644
--- a/homeassistant/components/calendar/google.py
+++ b/homeassistant/components/calendar/google.py
@@ -51,6 +51,10 @@ class GoogleCalendarEventDevice(CalendarEventDevice):
 
         super().__init__(hass, data)
 
+    async def async_get_events(self, hass, start_date, end_date):
+        """Get all events in a specific time frame."""
+        return await self.data.async_get_events(hass, start_date, end_date)
+
 
 class GoogleCalendarData(object):
     """Class to utilize calendar service object to get next event."""
@@ -64,9 +68,7 @@ class GoogleCalendarData(object):
         self.ignore_availability = ignore_availability
         self.event = None
 
-    @Throttle(MIN_TIME_BETWEEN_UPDATES)
-    def update(self):
-        """Get the latest data."""
+    def _prepare_query(self):
         from httplib2 import ServerNotFoundError
 
         try:
@@ -74,13 +76,41 @@ class GoogleCalendarData(object):
         except ServerNotFoundError:
             _LOGGER.warning("Unable to connect to Google, using cached data")
             return False
-
         params = dict(DEFAULT_GOOGLE_SEARCH_PARAMS)
-        params['timeMin'] = dt.now().isoformat('T')
         params['calendarId'] = self.calendar_id
         if self.search:
             params['q'] = self.search
 
+        return service, params
+
+    async def async_get_events(self, hass, start_date, end_date):
+        """Get all events in a specific time frame."""
+        service, params = await hass.async_add_job(self._prepare_query)
+        params['timeMin'] = start_date.isoformat('T')
+        params['timeMax'] = end_date.isoformat('T')
+
+        # pylint: disable=no-member
+        events = await hass.async_add_job(service.events)
+        # pylint: enable=no-member
+        result = await hass.async_add_job(events.list(**params).execute)
+
+        items = result.get('items', [])
+        event_list = []
+        for item in items:
+            if (not self.ignore_availability
+                    and 'transparency' in item.keys()):
+                if item['transparency'] == 'opaque':
+                    event_list.append(item)
+            else:
+                event_list.append(item)
+        return event_list
+
+    @Throttle(MIN_TIME_BETWEEN_UPDATES)
+    def update(self):
+        """Get the latest data."""
+        service, params = self._prepare_query()
+        params['timeMin'] = dt.now().isoformat('T')
+
         events = service.events()  # pylint: disable=no-member
         result = events.list(**params).execute()
 
diff --git a/homeassistant/components/calendar/todoist.py b/homeassistant/components/calendar/todoist.py
index b70e44456db822ca1014769df8be7d96bf3ab81d..71a6a17de107a87296d1d6da79c15d0cb06a2ac7 100644
--- a/homeassistant/components/calendar/todoist.py
+++ b/homeassistant/components/calendar/todoist.py
@@ -257,6 +257,10 @@ class TodoistProjectDevice(CalendarEventDevice):
         super().cleanup()
         self._cal_data[ALL_TASKS] = []
 
+    async def async_get_events(self, hass, start_date, end_date):
+        """Get all events in a specific time frame."""
+        return await self.data.async_get_events(hass, start_date, end_date)
+
     @property
     def device_state_attributes(self):
         """Return the device state attributes."""
@@ -485,6 +489,31 @@ class TodoistProjectData(object):
                     continue
         return event
 
+    async def async_get_events(self, hass, start_date, end_date):
+        """Get all tasks in a specific time frame."""
+        if self._id is None:
+            project_task_data = [
+                task for task in self._api.state[TASKS]
+                if not self._project_id_whitelist or
+                task[PROJECT_ID] in self._project_id_whitelist]
+        else:
+            project_task_data = self._api.projects.get_data(self._id)[TASKS]
+
+        events = []
+        time_format = '%a %d %b %Y %H:%M:%S %z'
+        for task in project_task_data:
+            due_date = datetime.strptime(task['due_date_utc'], time_format)
+            if due_date > start_date and due_date < end_date:
+                event = {
+                    'uid': task['id'],
+                    'title': task['content'],
+                    'start': due_date.isoformat(),
+                    'end': due_date.isoformat(),
+                    'allDay': True,
+                }
+                events.append(event)
+        return events
+
     @Throttle(MIN_TIME_BETWEEN_UPDATES)
     def update(self):
         """Get the latest data."""
diff --git a/tests/components/calendar/test_caldav.py b/tests/components/calendar/test_caldav.py
index 11dd0cb963535d88ebaedd82c25825b518c57cd8..c5dadbc56eaca214892525a4e0057a2cbb453345 100644
--- a/tests/components/calendar/test_caldav.py
+++ b/tests/components/calendar/test_caldav.py
@@ -19,7 +19,7 @@ _LOGGER = logging.getLogger(__name__)
 
 DEVICE_DATA = {
     "name": "Private Calendar",
-    "device_id": "Private Calendar"
+    "device_id": "Private Calendar",
 }
 
 EVENTS = [
@@ -163,6 +163,7 @@ class TestComponentsWebDavCalendar(unittest.TestCase):
     def setUp(self):
         """Set up things to be run when tests are started."""
         self.hass = get_test_home_assistant()
+        self.hass.http = Mock()
         self.calendar = _mock_calendar("Private")
 
     # pylint: disable=invalid-name
@@ -255,7 +256,7 @@ class TestComponentsWebDavCalendar(unittest.TestCase):
             "start_time": "2017-11-27 17:00:00",
             "end_time": "2017-11-27 18:00:00",
             "location": "Hamburg",
-            "description": "Surprisingly rainy"
+            "description": "Surprisingly rainy",
         })
 
     @patch('homeassistant.util.dt.now', return_value=_local_datetime(17, 30))
@@ -274,7 +275,7 @@ class TestComponentsWebDavCalendar(unittest.TestCase):
             "start_time": "2017-11-27 17:00:00",
             "end_time": "2017-11-27 18:00:00",
             "location": "Hamburg",
-            "description": "Surprisingly rainy"
+            "description": "Surprisingly rainy",
         })
 
     @patch('homeassistant.util.dt.now', return_value=_local_datetime(17, 00))
@@ -293,7 +294,7 @@ class TestComponentsWebDavCalendar(unittest.TestCase):
             "start_time": "2017-11-27 16:30:00",
             "description": "Sunny day",
             "end_time": "2017-11-27 17:30:00",
-            "location": "San Francisco"
+            "location": "San Francisco",
         })
 
     @patch('homeassistant.util.dt.now', return_value=_local_datetime(8, 30))
@@ -311,7 +312,7 @@ class TestComponentsWebDavCalendar(unittest.TestCase):
             "start_time": "2017-11-27 10:00:00",
             "end_time": "2017-11-27 11:00:00",
             "location": "Hamburg",
-            "description": "Surprisingly shiny"
+            "description": "Surprisingly shiny",
         })
 
     @patch('homeassistant.util.dt.now', return_value=_local_datetime(12, 00))
@@ -332,7 +333,7 @@ class TestComponentsWebDavCalendar(unittest.TestCase):
             "start_time": "2017-11-27 17:00:00",
             "end_time": "2017-11-27 18:00:00",
             "location": "Hamburg",
-            "description": "Surprisingly rainy"
+            "description": "Surprisingly rainy",
         })
 
     @patch('homeassistant.util.dt.now', return_value=_local_datetime(12, 00))
@@ -353,7 +354,7 @@ class TestComponentsWebDavCalendar(unittest.TestCase):
             "start_time": "2017-11-27 17:00:00",
             "end_time": "2017-11-27 18:00:00",
             "location": "Hamburg",
-            "description": "Surprisingly rainy"
+            "description": "Surprisingly rainy",
         })
 
     @patch('homeassistant.util.dt.now', return_value=_local_datetime(20, 00))
@@ -395,5 +396,5 @@ class TestComponentsWebDavCalendar(unittest.TestCase):
             "start_time": "2017-11-27 00:00:00",
             "end_time": "2017-11-28 00:00:00",
             "location": "Hamburg",
-            "description": "What a beautiful day"
+            "description": "What a beautiful day",
         })
diff --git a/tests/components/calendar/test_demo.py b/tests/components/calendar/test_demo.py
new file mode 100644
index 0000000000000000000000000000000000000000..50ac63121b1290bc9ceeab0366f8d45e324168bb
--- /dev/null
+++ b/tests/components/calendar/test_demo.py
@@ -0,0 +1,24 @@
+"""The tests for the demo calendar component."""
+from datetime import timedelta
+
+from homeassistant.bootstrap import async_setup_component
+import homeassistant.util.dt as dt_util
+
+
+async def test_api_calendar_demo_view(hass, aiohttp_client):
+    """Test the calendar demo view."""
+    await async_setup_component(hass, 'calendar',
+                                {'calendar': {'platform': 'demo'}})
+    client = await aiohttp_client(hass.http.app)
+    response = await client.get(
+        '/api/calendar/calendar_2')
+    assert response.status == 400
+    start = dt_util.now()
+    end = start + timedelta(days=1)
+    response = await client.get(
+        '/api/calendar/calendar_1?start={}&end={}'.format(start.isoformat(),
+                                                          end.isoformat()))
+    assert response.status == 200
+    events = await response.json()
+    assert events[0]['summary'] == 'Future Event'
+    assert events[0]['title'] == 'Future Event'
diff --git a/tests/components/calendar/test_google.py b/tests/components/calendar/test_google.py
index 9f94ea9f44c370bef56dde66713c6451c8ec7b34..d176cd758b43a90cef1066c8b4e6f5a340423580 100644
--- a/tests/components/calendar/test_google.py
+++ b/tests/components/calendar/test_google.py
@@ -27,6 +27,7 @@ class TestComponentsGoogleCalendar(unittest.TestCase):
     def setUp(self):
         """Setup things to be run when tests are started."""
         self.hass = get_test_home_assistant()
+        self.hass.http = Mock()
 
         # Set our timezone to CST/Regina so we can check calculations
         # This keeps UTC-6 all year round
@@ -99,7 +100,7 @@ class TestComponentsGoogleCalendar(unittest.TestCase):
             'start_time': '{} 00:00:00'.format(event['start']['date']),
             'end_time': '{} 00:00:00'.format(event['end']['date']),
             'location': event['location'],
-            'description': event['description']
+            'description': event['description'],
         })
 
     @patch('homeassistant.components.calendar.google.GoogleCalendarData')
@@ -160,7 +161,7 @@ class TestComponentsGoogleCalendar(unittest.TestCase):
                 (one_hour_from_now + dt_util.dt.timedelta(minutes=60))
                 .strftime(DATE_STR_FORMAT),
             'location': '',
-            'description': ''
+            'description': '',
         })
 
     @patch('homeassistant.components.calendar.google.GoogleCalendarData')
@@ -222,7 +223,7 @@ class TestComponentsGoogleCalendar(unittest.TestCase):
                 (middle_of_event + dt_util.dt.timedelta(minutes=60))
                 .strftime(DATE_STR_FORMAT),
             'location': '',
-            'description': ''
+            'description': '',
         })
 
     @patch('homeassistant.components.calendar.google.GoogleCalendarData')
@@ -285,7 +286,7 @@ class TestComponentsGoogleCalendar(unittest.TestCase):
                 (middle_of_event + dt_util.dt.timedelta(minutes=60))
                 .strftime(DATE_STR_FORMAT),
             'location': '',
-            'description': ''
+            'description': '',
         })
 
     @pytest.mark.skip
@@ -352,7 +353,7 @@ class TestComponentsGoogleCalendar(unittest.TestCase):
             'start_time': '{} 06:00:00'.format(event['start']['date']),
             'end_time': '{} 06:00:00'.format(event['end']['date']),
             'location': event['location'],
-            'description': event['description']
+            'description': event['description'],
         })
 
     @patch('homeassistant.components.calendar.google.GoogleCalendarData')
@@ -419,7 +420,7 @@ class TestComponentsGoogleCalendar(unittest.TestCase):
             'start_time': '{} 00:00:00'.format(event['start']['date']),
             'end_time': '{} 00:00:00'.format(event['end']['date']),
             'location': event['location'],
-            'description': event['description']
+            'description': event['description'],
         })
 
     @MockDependency("httplib2")